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

net.automatalib.util.automata.conformance.IncrementalWMethodTestsIterator Maven / Gradle / Ivy

Go to download

This artifact provides various common utility operations for analyzing and manipulating automata and graphs, such as traversal, minimization and copying.

There is a newer version: 0.11.0
Show newest version
/* Copyright (C) 2014 TU Dortmund
 * This file is part of AutomataLib, http://www.automatalib.net/.
 * 
 * AutomataLib is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 3.0 as published by the Free Software Foundation.
 * 
 * AutomataLib 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
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with AutomataLib; if not, see
 * http://www.gnu.de/documents/lgpl.en.html.
 */
package net.automatalib.util.automata.conformance;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;

import net.automatalib.automata.UniversalDeterministicAutomaton;
import net.automatalib.util.automata.Automata;
import net.automatalib.words.Alphabet;
import net.automatalib.words.Word;

/**
 * An iterator that enumerates the test cases as obtained through the W method
 * conformance test in an incremental fashion.
 * 
 * @author Malte Isberner
 *
 * @param  input symbol type
 */
public class IncrementalWMethodTestsIterator implements Iterator> {
	
	private final static class Item {
		private int prefixIdx;
		private int suffixIdx;
		private Word middle;
		
		private int minSuffix;
		private int minPrefix;
		private int maxPrefix;
		
		@Override
		public String toString() {
			return Integer.toString(prefixIdx) + " | " + middle + " | " + Integer.toString(suffixIdx);
		}
	}
	
	private static final class ItemMerge implements StrictPriorityQueue.MergeOperation> {

		@Override
		public Item merge(Item oldObject, Item newObject) {
			oldObject.minSuffix = Math.min(oldObject.minSuffix, newObject.minSuffix);
			oldObject.minPrefix = Math.min(oldObject.minPrefix, newObject.minPrefix);
			oldObject.maxPrefix = Math.max(oldObject.maxPrefix, newObject.maxPrefix);
			return oldObject;
		}
		
	}
	
	private static final class ItemComparator implements Comparator> {
		private final Comparator> canonicalCmp;
		
		public ItemComparator(Comparator symComparator) {
			this.canonicalCmp = Word.canonicalComparator(symComparator);
		}

		@Override
		public int compare(Item o1, Item o2) {
			int cmp = canonicalCmp.compare(o1.middle, o2.middle);
			if(cmp != 0) {
				return cmp;
			}
			
			cmp = o1.prefixIdx - o2.prefixIdx;
			if(cmp != 0) {
				return cmp;
			}
			
			return o1.suffixIdx - o2.suffixIdx;
		}
	}
	
	private final Alphabet alphabet;
	private final StrictPriorityQueue> itemQueue;
	
	private int maxDepth;
	
	private final List> prefixes = new ArrayList<>();
	private final List> suffixes = new ArrayList<>();

	public IncrementalWMethodTestsIterator(Alphabet alphabet) {
		this.alphabet = alphabet;
		this.itemQueue = new StrictPriorityQueue<>(new ItemComparator<>(alphabet), new ItemMerge());
		this.suffixes.add(Word.epsilon()); // *always* assume the empty word as a suffix
	}
	
	public int getMaxDepth() {
		return maxDepth;
	}
	
	public void setMaxDepth(int maxDepth) {
		this.maxDepth = maxDepth;
	}
	
	private Word startMiddleWord() {
		return Word.fromLetter(alphabet.getSymbol(0));
	}
	
	public void update(UniversalDeterministicAutomaton automaton) {
		int oldNumPrefixes = prefixes.size();
		boolean newPrefixes = Automata.incrementalStructuralCover(automaton, alphabet, prefixes, prefixes);
		
		int oldNumSuffixes = suffixes.size();
		boolean newSuffixes = Automata.incrementalCharacterizingSet(automaton, alphabet, suffixes, suffixes);
		
		// old prefixes with all *new* suffixes
		if(newSuffixes && oldNumPrefixes > 0) {
			Item item = new Item<>();
			item.prefixIdx = 0;
			item.minPrefix = 0;
			item.maxPrefix = oldNumPrefixes;
			item.suffixIdx = oldNumSuffixes;
			item.minSuffix = oldNumSuffixes;
			item.middle = startMiddleWord();
			itemQueue.insert(item);
		}
		// new prefixes with *all* suffixes
		if(newPrefixes) {
			Item item = new Item<>();
			item.prefixIdx = oldNumPrefixes;
			item.minPrefix = oldNumPrefixes;
			item.maxPrefix = prefixes.size();
			item.suffixIdx = 0;
			item.minSuffix = 0;
			item.middle = startMiddleWord();
			itemQueue.insert(item);
		}
	}
	
	@Override
	public boolean hasNext() {
		return !itemQueue.isEmpty();
	}
	
	@Override
	public void remove() {
		throw new UnsupportedOperationException();
	}
	
	@Override
	public Word next() {
		Item nextItem = itemQueue.extractMin();
		
		Word result = assembleWord(nextItem);
		Item inc = increment(nextItem);
		if(inc != null) {
			itemQueue.insert(inc);
		}
		return result;
	}
	
	private Item increment(Item item) {
		item.suffixIdx++;
		if(item.suffixIdx >= suffixes.size()) {
			item.suffixIdx = item.minSuffix;
			
			item.prefixIdx++;
			if(item.prefixIdx >= item.maxPrefix) {
				item.prefixIdx = item.minPrefix;
				
				item.middle = item.middle.canonicalNext(alphabet);
				if(item.middle.length() > maxDepth) {
					return null;
				}
			}
		}
		
		return item;
	}
	
	private Word assembleWord(Item item) {
		Word prefix = prefixes.get(item.prefixIdx);
		Word suffix = suffixes.get(item.suffixIdx);
		return prefix.concat(item.middle, suffix);
	}
	
	
	
}