// SPDX-FileCopyrightText: 2020 Roberto Posenato // // SPDX-License-Identifier: LGPL-3.0-or-later package it.univr.di.cstnu.graph; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import it.unimi.dsi.fastutil.objects.*; import it.unimi.dsi.fastutil.objects.Object2ObjectMap.Entry; import it.univr.di.Debug; import it.univr.di.labeledvalue.*; import javax.annotation.Nonnull; import java.io.Serial; import java.util.logging.Level; import java.util.logging.Logger; /** * An abstract implementation of BasicCSTNUEdge where the type to represent labeled value set can be plugged during the creation. * * @author posenato * @version $Rev$ */ public abstract class BasicCSTNUEdgePluggable extends CSTNEdgePluggable implements BasicCSTNUEdge { /** * Represents a pair (Label, String). * * @author posenato */ @SuppressWarnings({"CompareToUsesNonFinalVariable", "NonFinalFieldReferenceInEquals", "NonFinalFieldReferencedInHashCode"}) final static class InternalEntry implements Object2ObjectMap.Entry, Comparable> { /** * */ ALabel aLabel; /** * */ final Label label; /** * @param inputLabel propositional label * @param inputALabel alphabetic label */ InternalEntry(Label inputLabel, ALabel inputALabel) { label = inputLabel; aLabel = inputALabel; } @Override public int compareTo(@Nonnull Object2ObjectMap.Entry o) { if (this == o) { return 0; } final int i = label.compareTo(o.getKey()); if (i != 0) { return i; } return aLabel.compareTo(o.getValue()); } @Override public Label getKey() { return label; } @Override public ALabel getValue() { return aLabel; } @Override public ALabel setValue(ALabel value) { final ALabel old = ALabel.clone(aLabel); aLabel = value; return old; } @Override public int hashCode() { return label.hashCode() + 31 * aLabel.hashCode(); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof InternalEntry e)) { return false; } return label.equals(e.label) && aLabel.equals(e.aLabel); } @Override public String toString() { return "(" + aLabel + ", " + label + ")"; } } /** * */ @Serial private static final long serialVersionUID = 3L; // class initializer static { //actualize logger to this class. LOG = Logger.getLogger(BasicCSTNUEdgePluggable.class.getName()); } /** * The CSTNU controllability check algorithm needs to know if a labeled value has been already considered in the past in order to avoid to add it a second * time. */ Object2IntMap> consideredUpperCaseValue; /** * Morris Upper case value augmented by a propositional label. *
* The name of node has to be equal to the original name. No case modifications are necessary! */ LabeledALabelIntTreeMap upperCaseValue; /** * @param n name of edge */ BasicCSTNUEdgePluggable(String n) { this(n, null); } /** * @param n name of edge * @param labeledIntMapImpl the class for representing the labeled value maps. If null, then {@link #DEFAULT_LABELED_INT_MAP_CLASS} is used. */ public BasicCSTNUEdgePluggable(String n, Class labeledIntMapImpl) { super(n, labeledIntMapImpl); upperCaseValue = new LabeledALabelIntTreeMap(this.getLabeledIntMapImplClass()); consideredUpperCaseValue = new Object2IntArrayMap<>(); consideredUpperCaseValue.defaultReturnValue(Constants.INT_NULL); } /** * @param e edge to clone */ BasicCSTNUEdgePluggable(Edge e) { this(e, null); } /** * @param e edge to clone * @param labeledIntMapImpl the class for representing the labeled value maps. If null, then {@link #DEFAULT_LABELED_INT_MAP_CLASS} is used. */ BasicCSTNUEdgePluggable(Edge e, Class labeledIntMapImpl) { super(e, labeledIntMapImpl); upperCaseValue = new LabeledALabelIntTreeMap(this.getLabeledIntMapImplClass()); if (e != null && BasicCSTNUEdge.class.isAssignableFrom(e.getClass())) { final BasicCSTNUEdge e1 = (BasicCSTNUEdge) e; //add just upper-case values for (final Object2ObjectMap.Entry> entry : e1.getUpperCaseValues()) { upperCaseValue.putTriple(entry.getValue().getKey(), entry.getKey(), entry.getValue().getIntValue()); } } consideredUpperCaseValue = new Object2IntArrayMap<>(); consideredUpperCaseValue.defaultReturnValue(Constants.INT_NULL); } @Override public void clear() { super.clear(); upperCaseValue.clear(); consideredUpperCaseValue.clear(); } /** * */ @Override public boolean hasSameValues(Edge e) { if (!(e instanceof BasicCSTNUEdge e1)) { return false; } if (e == this) { return true; } if (!super.hasSameValues(e)) { return false; } return (upperCaseValue.entrySet().equals(e1.getUpperCaseValues())); } @Override public boolean isEmpty() { return super.isEmpty() && upperCaseValue.isEmpty(); } @Override public boolean mergeLabeledValue(Label l, int i) { final boolean added = super.mergeLabeledValue(l, i); if (added && !upperCaseValue.isEmpty()) { // I try to clean UPPER values // Since this.labeledValue.put(l, i) can simplify labeled value set, // it is necessary to check every UPPER CASE with any labeled value, not only the last one inserted! // 2017-10-31 I verified that it is necessary to improve the performance! // 2018-12-24 I re-verified that it is necessary to improve the performance! // int maxValueWOUpperCase = this.labeledValue.getMaxValue(); // if (this.labeledValue.size() >= nBeforeAdd && (this.labeledValue.get(l) == i)) { // the added element did not simplify the set, we compare UC values only with it. for (final ALabel UCALabel : upperCaseValue.aLabelSet()) { for (final Label UCLabel : upperCaseValue.labelSet(UCALabel)) { final int UCValue = this.getUpperCaseValue(UCLabel, UCALabel); // if (UCaseValue >= maxValueWOUpperCase) { // //this wait is useless because a normal constraint // this.putUpperCaseValueToRemovedList(UCLabel, UCALabel, UCaseValue); // this.removeUpperCaseValue(UCLabel, UCALabel); // continue; // } if (i <= UCValue && UCLabel.subsumes(l)) { if (Debug.ON) { if (CSTNEdgePluggable.LOG.isLoggable(Level.FINEST)) { CSTNEdgePluggable.LOG.log(Level.FINEST, "The value " + BasicCSTNUEdgePluggable.pairAsString(l, i) + " makes redundant upper case value " + BasicCSTNUEdgePluggable.upperCaseValueAsString(UCALabel, UCValue, UCLabel) + ". Last one is removed."); } } setUpperCaseValueAsConsidered(UCLabel, UCALabel, UCValue); removeUpperCaseValue(UCLabel, UCALabel); } } } // } else { // // this.labeledvalue set has been simplified. We consider all values of this.labeledvalue // 2018-12-17 Too much expensive, we check only the new inserted value. // for (ALabel UCALabel : upperCaseValueValueMap.aLabelSet()) { // LabeledIntTreeMap labeledUCValues = upperCaseValueValueMap.get(UCALabel); // for (Label UCLabel : labeledUCValues.aLabelSet()) { // int UCaseValue = labeledUCValues.get(UCLabel); // int min = this.labeledValue.getMinValueSubsumedBy(UCLabel); // if (min == Constants.INT_NULL) // continue; // if (min <= UCaseValue) { // this.putUpperCaseValueToRemovedList(UCLabel, UCALabel, UCaseValue); // this.removeUpperCaseValue(UCLabel, UCALabel); // } // } // } // } } return added; } /** * @param label input * @param value input * * @return the conventional representation of a labeled value */ static String pairAsString(Label label, int value) { return AbstractLabeledIntMap.entryAsString(label, value); } /** * @param value value * @param nodeName node name to put as upper case label * @param label label to represent * * @return the conventional representation of a labeled value */ static String upperCaseValueAsString(ALabel nodeName, int value, Label label) { return LabeledALabelIntTreeMap.entryAsString(label, value, nodeName); } /** * Set the triple as already considered in order to avoid to consider it again in the future. * * @param l label * @param n name of node as a-label * @param i edge weight * * @return the old value associated to (l,n), or the {@link Constants#INT_NULL} if no value was present. */ @SuppressWarnings("UnusedReturnValue") final int setUpperCaseValueAsConsidered(Label l, ALabel n, int i) { return consideredUpperCaseValue.put(new InternalEntry(l, n), i); } /** * */ @Override public final boolean putLabeledValue(Label l, int i) { // once a value has been inserted, it is useless to insert it again in the future. consideredLabeledValue.put(l, i); return labeledValue.put(l, i); } @Override public int size() { return super.size() + upperCaseValueSize() + lowerCaseValueSize(); } /** * */ @Override public void takeIn(Edge e) { if (e == null) { return; } super.takeIn(e); if (e instanceof BasicCSTNUEdgePluggable e1) { upperCaseValue = e1.upperCaseValue; consideredUpperCaseValue = e1.consideredUpperCaseValue; } } /** * Return a string representation of labeled values.h */ @SuppressFBWarnings(value = "RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE", justification = "False positive.") @Nonnull @Override public String toString() { final StringBuilder superS = new StringBuilder(super.toString()); superS.delete(superS.length() - Constants.CLOSE_TUPLE.length(), superS.length()); if (upperCaseValueSize() > 0) { superS.append("UL: ").append(upperCaseValuesAsString()).append("; "); } if (lowerCaseValueSize() > 0) { superS.append("LL: ").append(lowerCaseValueAsString()).append("; "); } superS.append(Constants.CLOSE_TUPLE); return superS.toString(); } @Override public final void clearUpperCaseValues() { upperCaseValue.clear(); } @Override public final ObjectSet>> getAllUpperCaseAndLabeledValues() { final ObjectSet>> result = getUpperCaseValues(); for (final Object2IntMap.Entry