r2151 - in trunk/nuiton-utils: . src/main/java/org/nuiton/util src/test/java/org/nuiton/util
Author: bpoussin Date: 2011-06-21 13:00:42 +0200 (Tue, 21 Jun 2011) New Revision: 2151 Url: http://nuiton.org/repositories/revision/nuiton-utils/2151 Log: Implantation de Map multikey en se basant sur le code des matrix (test a ameliorer/faire) (commit pour ne pas perdre le travail) Added: trunk/nuiton-utils/src/main/java/org/nuiton/util/MatrixMap.java trunk/nuiton-utils/src/test/java/org/nuiton/util/MatrixMapTest.java Modified: trunk/nuiton-utils/pom.xml Modified: trunk/nuiton-utils/pom.xml =================================================================== --- trunk/nuiton-utils/pom.xml 2011-06-16 08:31:43 UTC (rev 2150) +++ trunk/nuiton-utils/pom.xml 2011-06-21 11:00:42 UTC (rev 2151) @@ -41,6 +41,11 @@ <dependencies> <dependency> + <groupId>commons-lang</groupId> + <artifactId>commons-lang</artifactId> + </dependency> + + <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> </dependency> Added: trunk/nuiton-utils/src/main/java/org/nuiton/util/MatrixMap.java =================================================================== --- trunk/nuiton-utils/src/main/java/org/nuiton/util/MatrixMap.java (rev 0) +++ trunk/nuiton-utils/src/main/java/org/nuiton/util/MatrixMap.java 2011-06-21 11:00:42 UTC (rev 2151) @@ -0,0 +1,2296 @@ +package org.nuiton.util; + +import java.io.Serializable; +import java.util.AbstractList; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.RandomAccess; +import org.apache.commons.lang.ObjectUtils; +import org.apache.commons.lang.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Permet de stocker des informations dans une matrix a N dimension + * Si lors de l'ajout on indique une dimension qui n'existe pas encore ou + * un element dans une dimension qui n'existe pas, la matrice ajoute + * automatiquement les elements manquant pour que l'ajout se passe bien. + * <p> + * MatrixMap permet de stocker les elements avec des cles de n'importe quel + * type. Les coordonnees utilisant ces objets sont converti en coordonnees + * numeriques qui est la seul chose que sait gere Matrix. Ces coordonnees + * numeriques sont alors convertis en coordonnees lineaire pour le stockage + * dans Vector. On decoupe ainsi les problemes et on minimise le stockage et + * certain traitement sur les données puisqu'au final toutes les données sont + * dans une simple liste. + * <p> + * Pour créer une nouvelle matrice, il faut utiliser une des méthodes de + * {@link MatrixMap#Factory} + * + * @author poussin + * @version $Revision$ + * @since 2.2.1 + * + * Last update: $Date$ + * by : $Author$ + */ +public interface MatrixMap<E> extends Iterable<E> { + + /** + * Classe permettant la creation de matrice + */ + static public class Factory { + static public <T> MatrixMap<T> create(List ... semantics) { + MatrixMap<T> result = new MatrixMapFixed<T>(semantics); + return result; + } + + static public <T> MatrixMap<T> create(String name, List... semantics) { + MatrixMap<T> result = new MatrixMapFixed<T>(name, semantics); + return result; + } + + static public <T> MatrixMap<T> create(String name, String[] dimNames, List... semantics) { + MatrixMap<T> result = new MatrixMapFixed<T>(name, dimNames, semantics); + return result; + } + + static public <T> MatrixMap<T> create(MatrixMap<T> matrix) { + MatrixMap<T> result = new MatrixMapFixed<T>(matrix); + return result; + } + + static public <T> MatrixMap<T> createElastic(List ... semantics) { + MatrixMap<T> result = create(semantics); + result = createElastic(result); + return result; + } + + static public <T> MatrixMap<T> createElastic(String name, List... semantics) { + MatrixMap<T> result = create(name, semantics); + result = createElastic(result); + return result; + } + + static public <T> MatrixMap<T> createElastic(String name, String[] dimNames, List... semantics) { + MatrixMap<T> result = create(name, dimNames, semantics); + result = createElastic(result); + return result; + } + + static public <T> MatrixMap<T> createElastic(MatrixMap<T> matrix) { + MatrixMap<T> result = new MatrixMapElastic<T>(matrix); + return result; + } + } + + @Override + public MatrixMapIterator<E> iterator(); + + /** + * Copy la matrice pour pouvoir la modifier sans perdre les donnees + * initiales. + * + * @return new matrix + */ + public MatrixMap<E> copy(); + + public SemanticList[] getSemantics(); + + public SemanticList getSemantic(int dim); + + public void setSemantic(int dim, List sem); + + public void setName(String name); + + public String getName(); + + public String[] getDimensionNames(); + + public void setDimensionNames(String[] names); + + public void setDimensionName(int dim, String name); + + public String getDimensionName(int dim); + + public int getDimCount(); + + public int[] getDim(); + + public int getDim(int d); + + /** + * Applique sur chaque element de la matrice la fonction f + * + * @param f la fonction a appliquer + * @return Retourne la matrice elle meme. Les modifications sont faites directement + * dessus + */ + public MatrixMap<E> map(MapFunction<E> f); + + /** + * Retourne l'element a une certaine position en utilisant des indices + * ex: 2,3,1 + * @param coordinates + * @return + */ + public E getValueIndex(int ... coordinates); + + /** + * Modifie l'element a une certaine position en utilisant des indices + * ex: 2,3,1 + * @param value la nouvelle valeur + * @param coordinates + * @return + */ + public void setValueIndex(E value, int ... coordinates); + + /** + * Retourne l'element a une certaine position en utilisant les semantiques + * + * @param coordinates + * @return + */ + public E getValue(Object ... coordinates); + + /** + * Modifie l'element a une certaine position en utilisant les semantiques + * + * @param value la nouvelle valeur + * @param coordinates + * @return + */ + public void setValue(E value, Object ... coordinates); + + /** + * Verifie que deux matrices sont completement equals + * (dimension, semantique, nom, valeur, ...) + * @param mat + * @return + */ + public boolean equals(MatrixMap mat); + + /** + * Verifie si les matrices sont egales en ne regardant que les valeurs et + * pas les semantiques + * + * @param mat + * @return equality on values + */ + public boolean equalsValues(MatrixMap<E> mat); + + /** + * Representation string de la matrice quelque soit le nombre de dimension + * @return + */ + public String toStringGeneric(); + + /** + * Indique si les semantiques passées en argument sont valable pour la + * matrice courante + * + * @param semantics + * @return + */ + public boolean isValidCoordinates(Object[] semantics); + + /** + * Copie une matrice dans la matrice actuelle. La matrice à copier à le même + * nombre de dimension. Si la matrice à copier est trop grande seul les + * éléments pouvant être copier le seront. + * + * @param mat la matrice à copier + * @return return la matrice courante. + */ + public MatrixMap paste(MatrixMap<E> mat); + + /** + * Permet de prendre une sous matrice dans la matrice courante. La sous + * matrice a le même nombre de dimensions mais sur une des dimensions on ne + * prend que certain élément. + * + * @param dim la dimension dans lequel on veut une sous matrice + * @param start la position dans dim d'ou il faut partir pour prendre la + * sous matrice. 0 <= start < dim.size si start est négatif alors + * la position de départ est calculé par rapport à la fin de la + * dimension, pour avoir le dernier élément il faut passer -1 + * @param nb le nombre d'élément à prendre dans la dimension si nb est + * inférieur ou égal à 0 alors cela indique qu'il faut prendre + * tous les éléments jusqu'à la fin de la dimension. + * @return new matrix + */ + public MatrixMap<E> getSubMatrix(int dim, Object start, int nb); + + /** + * Permet de prendre une sous matrice dans la matrice courante. La sous + * matrice a le même nombre de dimensions mais sur une des dimensions on ne + * prend que certain élément. + * + * @param dim la dimension dans lequel on veut une sous matrice + * @param elem les éléments dans la dimension à conserver + * @return new matrix + */ + public MatrixMap<E> getSubMatrix(int dim, Object... elem); + + /** + * Permet de prendre une sous matrice dans la matrice courante. + * + * Réalise plusieurs appels à {@link #getSubMatrix(int, Object...)} suivant + * l'implémentation. + * + * @param elem les éléments dans la dimension à conserver + * @return new matrix + */ + public MatrixMap<E> getSubMatrix(Object[]... elems); + + /** + * Reduit la matrice de sorte que toutes les dimensions qui n'ont qu'un + * élement soit supprimée. Au pire cette méthode retourne une matrice à une + * seule dimension à un seul élément. + * + * @return une nouvelle matrice plus petite que la matrice actuelle ou egal + * s'il n'y a aucune dimension à supprimer + */ + public MatrixMap<E> reduce(); + + /** + * Reduit le matrice seulement sur les dimensions passées en argument. Si + * une des dimensions passées en arguement n'a pas qu'un seul élément, cette + * dimension n'est pas prise en compte. + * + * @param dims les dimensions sur lequel il faut faire la reduction + * @return une nouvelle matrice + */ + public MatrixMap<E> reduceDims(int... dims); + /** + * Reduit la matrice de sorte que toutes les dimensions qui n'ont qu'un + * élement soit supprimée. Au pire cette méthode retourne une matrice à une + * seule dimension à un seul élément. + * + * @param minNbDim le nombre minimum de dimension que l'on souhaite pour la + * matrice résultat + * @return une nouvelle matrice plus petite que la matrice actuelle ou egal + * s'il n'y a aucune dimension à supprimer + */ + public MatrixMap<E> reduce(int minNbDim); + + /** + * Permet de retourner une nouvelle matrice ayant les semantiques passées + * en parametre. La nouvelle matrice contient les données de l'ancienne + * matrice par copie en fonction des semantiques + * + * @param sems + * @return + */ + public MatrixMap<E> extend(Object... sems); + + /////////////////////////////////////////////////////////////////////////// + // + // C L A S S E I N T E R N E + // + /////////////////////////////////////////////////////////////////////////// + + /** + * Classe contenant des méthodes statiques pour aider a la manipulation + * des matrices + */ + static public class MatrixHelper { + + /** + * Mais en forme un texte pour qu'il fasse exactement la longueur + * demandee (length). Si length est possitif alors s'il y besoin + * d'ajouter des espaces, ils seront mis devant le texte, sinon il + * seront mis apres le texte + * + * @param o l'objet a convertir en string + * @param length la longueur de representation souhaite + * @param valueIfNull la valeur a utilise si l'objet est null + * @return + */ + public static String format(Object o, int length, String valueIfNull) { + if (o == null) { + o = valueIfNull; + } + int absLength = Math.abs(length); + + String result = String.valueOf(o); + if (absLength > 3) { + result = StringUtils.abbreviate(result, absLength); + } + if (length < 0) { + result = StringUtils.leftPad(result, absLength); + } else if (length > 0) { + result = StringUtils.rightPad(result, absLength); + } + + return result; + } + + /** + * Permet de convertir des coordonnées définies par des entiers en coordonnées + * semantique par des objets + * + * @param semantics la semantique à utilisé pour la conversion + * @param coordinates les coordonnées à convertir + * @return un tableau donnant les coordonnées sous forme semantique s'il n'y + * a pas de semantique (liste pleine de null) alors un objet Integer + * est créer pour représenter la semantique de la dimension. + */ + public static Object[] dimensionToSemantics(List[] semantics, + int[] coordinates) { + Object[] result = new Object[coordinates.length]; + for (int i = 0; i < result.length; i++) { + result[i] = semantics[i].get(coordinates[i]); + } + return result; + } + + /** + * Permet de convertir des coordonnées sémantiques en coordonnées défini par + * des entiers. Cette fonction est l'inverse de + * {@link #dimensionToSemantics}. + * + * @param semantics la semantique à utiliser pour la conversion + * @param coordinates les coordonnées sémantique + * @return les coordonnées en entier. + */ + public static int[] semanticsToDimension(List[] semantics, + Object[] coordinates) { + int[] result = new int[coordinates.length]; + for (int i = 0; i < coordinates.length; i++) { + result[i] = indexOf(semantics, i, coordinates[i]); + } + return result; + } + + /** + * Permet de retrouver la position d'un objet dans une liste + * + * @param semantics la semantique à utilisé pour la recherche + * @param dim la dimension dans lequel il faut faire la recherche + * @param o l'objet à rechercher + * @return la position de l'objet dans la dimension demandée + * + * @throws NoSuchElementException If element doesn't exists + */ + public static int indexOf(List[] semantics, int dim, Object o) + throws NoSuchElementException { + int result = -1; + if ((0 <= dim) && (dim < semantics.length)) { + result = semantics[dim].indexOf(o); + } + if (result == -1) { + throw new NoSuchElementException( + "L'objet passé en argument n'a pas été retrouvé ou la dimension donnée ne convient pas:" + + o + " in " + semantics[dim]); + } + return result; + } + + /** + * Permet de savoir si deux dimension sont identiques. + * + * @param dim1 first dimensions + * @param dim2 second dimensions + * @return dimension equality + */ + public static boolean sameDimension(int[] dim1, int[] dim2) { + return Arrays.equals(dim1, dim2); + } + + } + + /** + * Iterateur de matrice + * + * @param <E> + */ + static public interface MatrixMapIterator<E> extends Iterator<E> { + public int[] getCoordinates(); + public E getValue(); + public void setValue(E value); + public Object[] getSemanticsCoordinates(); + } + + static public class MatrixMapIteratorImpl<E> implements MatrixMapIterator<E> { // MatrixMapIteratorImpl + + protected MatrixIterator<E> iterator = null; + protected List[] semantics = null; + protected int pos = 0; + + /** + * @param iterator la matrice sur lequel l'iterator doit travailler + * @param semantics la semantique de matrix, si matrix n'a pas de semantique + * alors il faut passer null + */ + public MatrixMapIteratorImpl(MatrixIterator<E> iterator, List[] semantics) { + this.iterator = iterator; + this.semantics = semantics; + pos = 0; + } + + @Override + public boolean hasNext() { + return iterator.hasNext(); + } + + @Override + public E next() { + return iterator.next(); + } + + @Override + public void remove() { + iterator.remove(); + } + + public int[] getCoordinates() { + return iterator.getCoordinates(); + } + + public E getValue() { + return iterator.getValue(); + } + + public void setValue(E value) { + iterator.setValue(value); + } + + public Object[] getSemanticsCoordinates() { + Object[] result = null; + if (semantics != null) { + int[] coordinates = getCoordinates(); + result = MatrixHelper.dimensionToSemantics(semantics, + coordinates); + } + return result; + } + + } // MatrixMapIteratorImpl + + /** + * Collection particuliere utilisee pour la stockage des semantiques. + * <p> + * Sert a optimiser la recherche de la position d'une donnee dans la liste. + * Permet aussi de verifier qu'on ajoute pas de doublon dans la liste + * + * @param <T> + */ + public static class SemanticList<T> extends AbstractList<T> implements RandomAccess { + + protected ArrayList<T> datas = null; + protected Map<T, Integer> index = new HashMap<T, Integer>(); + + public SemanticList() { + this(new ArrayList<T>()); + } + + public SemanticList(Collection<T> c) { + datas = new ArrayList<T>(c); + } + + /* + * @see java.util.AbstractList#get(int) + */ + @Override + public T get(int index) { + T result = datas.get(index); + return result; + } + + @Override + public void add(int index, T element) { + datas.add(index, element); + this.index.clear(); + } + + @Override + public T set(int index, T element) { + T result = datas.set(index, element); + this.index.clear(); + return result; + } + + @Override + public T remove(int index) { + T result = super.remove(index); + this.index.clear(); + return result; + } + + + /* + * @see java.util.AbstractCollection#size() + */ + @Override + public int size() { + int result = datas.size(); + return result; + } + + /* + * @see java.util.AbstractList#indexOf(java.lang.Object) + */ + @Override + public int indexOf(Object o) { + Map<T, Integer> index = getIndex(); + Integer result = index.get(o); + int resultIndex = -1; + if (result != null) { + resultIndex = result.intValue(); + } + return resultIndex; + } + + protected Map<T, Integer> getIndex() { + if (index.isEmpty()) { + for (int i = 0; i < datas.size(); i++) { + index.put(datas.get(i), Integer.valueOf(i)); + } + } + return index; + } + } + + /** + * Implantation particuliere de matrice, qui lorsqu'on lui passe des + * dimension qui n'existe pas, elle les ajoutes dans les semantiques. Ceci + * n'est vrai que pour le set avec des semantiques, le set avec des indices + * ne rend pas la matrice elastique. + * <p> + * Cette classe fonctionne avec une matrice interne que l'on change lorsque + * l'on a besoin de modifier les dimensions. Le changement de dimension + * a donc un cout (creation d'une nouvelle matrice, copie des elements) + * <p> + * Si on cree une sous matrice, et que l'on modifie la matrice mere + * La sous matrice n'est pas impacter, puisqu'elle est base sur l'ancienne + * represention interne de la matrice elastique, les deux matrices n'ont donc + * plus de lien. + * <p> + * Les methodes reduce et extend retourne de nouvelle matrice qui ne sont + * pas elastique. Si on veut qu'elle le soit, il faut les reencapsuler + * + * @param <E> + */ + static public class MatrixMapElastic<E> implements MatrixMap<E> { + + protected MatrixMap<E> internalMatrixMap; + + public MatrixMapElastic() { + internalMatrixMap = Factory.create(); + } + + public MatrixMapElastic(MatrixMap<E> m) { + setInternalMatrixMap(m); + } + + public MatrixMap<E> getInternalMatrixMap() { + return internalMatrixMap; + } + + public void setInternalMatrixMap(MatrixMap<E> internalMatrixMap) { + this.internalMatrixMap = internalMatrixMap; + } + + public MatrixMapIterator<E> iterator() { + return getInternalMatrixMap().iterator(); + } + + public MatrixMap<E> copy() { + return getInternalMatrixMap().copy(); + } + + public SemanticList[] getSemantics() { + return getInternalMatrixMap().getSemantics(); + } + + public SemanticList getSemantic(int dim) { + return getInternalMatrixMap().getSemantic(dim); + } + + public void setSemantic(int dim, List sem) { + getInternalMatrixMap().setSemantic(dim, sem); + } + + public void setName(String name) { + getInternalMatrixMap().setName(name); + } + + public String getName() { + return getInternalMatrixMap().getName(); + } + + public String[] getDimensionNames() { + return getInternalMatrixMap().getDimensionNames(); + } + + public void setDimensionNames(String[] names) { + getInternalMatrixMap().setDimensionNames(names); + } + + public void setDimensionName(int dim, String name) { + getInternalMatrixMap().setDimensionName(dim, name); + } + + public String getDimensionName(int dim) { + return getInternalMatrixMap().getDimensionName(dim); + } + + public int getDimCount() { + return getInternalMatrixMap().getDimCount(); + } + + public int[] getDim() { + return getInternalMatrixMap().getDim(); + } + + public int getDim(int d) { + return getInternalMatrixMap().getDim(d); + } + + public MatrixMap<E> map(MapFunction<E> f) { + return getInternalMatrixMap().map(f); + } + + public E getValueIndex(int... coordinates) { + return getInternalMatrixMap().getValueIndex(coordinates); + } + + public void setValueIndex(E value, int... coordinates) { + // la matrice est elastique que pour le set avec des semantics + getInternalMatrixMap().setValueIndex(value, coordinates); + } + + public E getValue(Object... coordinates) { + return getInternalMatrixMap().getValue(coordinates); + } + + public void setValue(E value, Object... coordinates) { + // check si les coordonnees sont valide. + // si non valide alors on extend la matrice interne + // et on appelle sur la nouvelle matrice interne + if (!isValidCoordinates(coordinates)) { + MatrixMap<E> newMatrixMap = getInternalMatrixMap().extend(coordinates); + setInternalMatrixMap(newMatrixMap); + } + getInternalMatrixMap().setValue(value, coordinates); + } + + @Override + public boolean equals(Object obj) { + return getInternalMatrixMap().equals(obj); + } + + public boolean equals(MatrixMap mat) { + return getInternalMatrixMap().equals(mat); + } + + public boolean equalsValues(MatrixMap<E> mat) { + return getInternalMatrixMap().equalsValues(mat); + } + + @Override + public String toString() { + return getInternalMatrixMap().toString(); + } + + public String toStringGeneric() { + return getInternalMatrixMap().toStringGeneric(); + } + + public boolean isValidCoordinates(Object[] semantics) { + return getInternalMatrixMap().isValidCoordinates(semantics); + } + + public MatrixMap paste(MatrixMap<E> mat) { + return getInternalMatrixMap().paste(mat); + } + + public MatrixMap<E> getSubMatrix(int dim, Object start, int nb) { + return getInternalMatrixMap().getSubMatrix(dim, start, nb); + } + + public MatrixMap<E> getSubMatrix(int dim, Object... elem) { + return getInternalMatrixMap().getSubMatrix(dim, elem); + } + + public MatrixMap<E> getSubMatrix(Object[]... elems) { + return getInternalMatrixMap().getSubMatrix(elems); + } + + public MatrixMap<E> reduce() { + return getInternalMatrixMap().reduce(); + } + + public MatrixMap<E> reduceDims(int... dims) { + return getInternalMatrixMap().reduceDims(dims); + } + + public MatrixMap<E> reduce(int minNbDim) { + return getInternalMatrixMap().reduce(minNbDim); + } + + public MatrixMap<E> extend(Object... sems) { + return getInternalMatrixMap().extend(sems); + } + + } + + /** + * Implantation de MatrixMap dont les dimensions sont fixees a la creation + * Les dimensions ne change plus par la suite + */ + static public class MatrixMapFixed<E> extends AbstractMatrixMap<E> { + /** to use log facility, just put in your code: log.info(\"...\"); */ + static private Log log = LogFactory.getLog(MatrixMapFixed.class); + + protected Matrix<E> matrix = null; + + public MatrixMapFixed(List ... semantics) { + super(semantics); + } + + public MatrixMapFixed(String name, List... semantics) { + this(semantics); + setName(name); + } + + public MatrixMapFixed(String name, String[] dimNames, List... semantics) { + this(name, semantics); + for (int i = 0; dimNames != null && i < dimNames.length; i++) { + setDimensionName(i, dimNames[i]); + } + } + + public MatrixMapFixed(MatrixMap<E> matrix) { + this(matrix.getName(), matrix.getDimensionNames(), matrix.getSemantics()); + this.pasteIndex(matrix); + } + + protected Matrix<E> getMatrix(){ + if (matrix == null) { + matrix = new Matrix<E>(getDim()); + } + return matrix; + } + + @Override + public MatrixMapIterator<E> iterator() { + return new MatrixMapIteratorImpl<E>(getMatrix().iterator(), getSemantics()); + } + + @Override + public MatrixMap<E> map(MapFunction<E> f) { + getMatrix().data.map(f); + return this; + } + + @Override + public E getValueIndex(int ... coordinates) { + if (coordinates.length == 0) { + throw new IllegalArgumentException("Coordinates must not be empty"); + } + return getMatrix().getValue(coordinates); + } + + /** + * Modifie un element de la matrice en fonction des dimensions passé en + * paramètre.<br> + * + * Exemple: Si on a un matrice 3D.<br> + * m.set(v, [1,1,1]) modifie un element de la matrice.<br> + * + * @param dimensions les différentes dimension à extraire. + * + * @param d l'entier double qui doit remplacer l'entier double spécifié par + * l'argument dimensions + */ + @Override + public void setValueIndex(E value, int ... coordinates) { + if (coordinates.length == 0) { + throw new IllegalArgumentException("Coordinates must not be empty"); + } + getMatrix().setValue(coordinates, value); + } + + /** + * Copie une matrice dans la matrice actuelle. La matrice à copier à le même + * nombre de dimension. Si la matrice à copier est trop grande seul les + * éléments pouvant être copier le seront. + * + * @param origin le point à partir duquel il faut faire la copie + * @param mat la matrice à copier + * @return return la matrice courante. + */ + public MatrixMap<E> paste(int[] origin, MatrixMap<E> mat) { + if (mat != null) { + // si les matrice mat et this on les memes dimensions + // et que origin est 0, on optimise en appeler une methode paste + // sur Matrix qui l'appel sur le vector + + // permet de savoir si l'origin est bien le point 0 de la matrice + boolean origin0 = true; + for (int i = 0; i < origin.length && origin0; i++) { + origin0 = origin0 && origin[i] == 0; + } + if (origin0 + && mat instanceof MatrixMapFixed + && Arrays.equals(mat.getDim(), this.getDim())) { + getMatrix().data.paste(((MatrixMapFixed<E>)mat).getMatrix().data); + } else { + super.paste(origin, mat); + } + } + return this; + } + + } + + /** + * Classe abstraite permettant de facilement implanter les matrice fixe, + * elastique et submatrix + * + * @param <E> + */ + static public abstract class AbstractMatrixMap<E> implements MatrixMap<E> { + + /** to use log facility, just put in your code: log.info(\"...\"); */ + static private Log log = LogFactory.getLog(AbstractMatrixMap.class); + + protected String name = null; + + protected String[] dimNames = null; + + protected int[] dim = null; + + protected SemanticList[] semantics = null; + + protected void init(int[] dim) { + this.dim = new int[dim.length]; + System.arraycopy(dim, 0, this.dim, 0, dim.length); + semantics = new SemanticList[dim.length]; + dimNames = new String[dim.length]; + } + + protected AbstractMatrixMap(int[] dim) { + init(dim); + for (int i = 0; i < getDimCount(); i++) { + // par defaut les listes des semantiques contiennent des nulls + // FIXME no multiple null allowed + setSemantic(i, Collections.nCopies(dim[i], null)); + } + } + + public AbstractMatrixMap(List ... semantics) { + int[] dim = new int[semantics.length]; + for (int i = 0; i < dim.length; i++) { + if (semantics[i] == null) { + dim[i] = 0; + } else { + dim[i] = semantics[i].size(); + } + } + init(dim); + for (int i = 0; i < getDimCount(); i++) { + setSemantic(i, semantics[i]); + } + } + + protected AbstractMatrixMap(String name, int[] dim) { + this(dim); + setName(name); + } + + protected AbstractMatrixMap(String name, int[] dim, String[] dimNames) { + this(dim); + setName(name); + for (int i = 0; dimNames != null && i < dimNames.length; i++) { + setDimensionName(i, dimNames[i]); + } + } + + public AbstractMatrixMap(String name, List... semantics) { + this(semantics); + setName(name); + } + + public AbstractMatrixMap(String name, String[] dimNames, List... semantics) { + this(name, semantics); + for (int i = 0; dimNames != null && i < dimNames.length; i++) { + setDimensionName(i, dimNames[i]); + } + } + + public AbstractMatrixMap(MatrixMap<E> matrix) { + this(matrix.getName(), matrix.getDimensionNames(), matrix.getSemantics()); + this.pasteIndex(matrix); + } + + /** + * Copy la matrice pour pouvoir la modifier sans perdre les donnees + * initiales. + * + * @return new matrix + */ + public MatrixMap<E> copy() { + MatrixMap<E> result = new MatrixMapFixed<E>(this); + return result; + } + + /* + * @see java.lang.Object#clone() + */ + @Override + public MatrixMap clone() { + return copy(); + } + + public SemanticList[] getSemantics() { + return semantics; + } + + public SemanticList getSemantic(int dim) { + return semantics[dim]; + } + + public void setSemantic(int dim, List sem) { + // make copy because this matrix can change semantics + SemanticList l = new SemanticList(sem); + semantics[dim] = l; + } + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public String[] getDimensionNames() { + return dimNames; + } + + public void setDimensionNames(String[] names) { + for (int i = 0; names != null && i < names.length; i++) { + setDimensionName(i, names[i]); + } + } + + public void setDimensionName(int dim, String name) { + dimNames[dim] = name; + } + + public String getDimensionName(int dim) { + return dimNames[dim]; + } + + public int getDimCount() { + return dim.length; + } + + public int[] getDim() { + return dim; + } + + public int getDim(int d) { + return dim[d]; + } + + /** + * Retourne la matrice elle meme. Les modifications sont faites directement + * dessus + */ + @Override + public MatrixMap<E> map(MapFunction<E> f) { + for (MatrixMapIterator<E> i = iterator(); i.hasNext();) { + i.setValue(f.apply(i.next())); + } + return this; + } + + public E getValue(Object ... coordinates) { + if (coordinates.length == 0) { + throw new IllegalArgumentException("Coordinates must not be empty"); + } + int[] intCoordinates = + MatrixHelper.semanticsToDimension(getSemantics(), coordinates); + E result = getValueIndex(intCoordinates); + return result; + } + + public void setValue(E value, Object ... coordinates) { + if (coordinates.length == 0) { + throw new IllegalArgumentException("Coordinates must not be empty"); + } + int[] intCoordinates = + MatrixHelper.semanticsToDimension(getSemantics(), coordinates); + setValueIndex(value, intCoordinates); + } + + // TODO peut-etre faire une variante de equals qui regarde par rapport au + // coordonnées sémantique + @Override + public boolean equals(Object o) { + return o instanceof MatrixMap && equals((MatrixMap) o); + } + + public boolean equals(MatrixMap mat) { + boolean result = true; + // le nom doit être le même + result = result && getName().equals(mat.getName()); + + result = result && equalsValues(mat); + + // les sémantiques doivent-être identique + for (int i = 0; result && i < getDimCount(); i++) { + String dimName1 = getDimensionName(i); + String dimName2 = mat.getDimensionName(i); + result = ObjectUtils.equals(dimName1, dimName2); + if (log.isTraceEnabled()) { + log.trace("dimName1(" + dimName1 + ")==dimName2(" + dimName2 + + ")=" + result); + } + // System.out.println("dimName1("+dimName1+")==dimName2("+dimName2+ + // ")="+result); + + List sem1 = getSemantic(i); + List sem2 = mat.getSemantic(i); + result = result && ObjectUtils.equals(sem1, sem2); + if (log.isTraceEnabled()) { + log.trace("sem1(" + sem1 + ")==sem2(" + sem2 + ")=" + result); + } + // System.out.println("sem1("+sem1+")==sem1("+sem2+ ")="+result); + } + + if (log.isTraceEnabled()) { + log.trace("result=" + result); + } + // System.out.println("result="+result); + return result; + } + + /** + * Verifie si les matrices sont egales en ne regardant que les valeurs et + * pas les semantiques + * + * @param mat + * @return equality on values + */ + public boolean equalsValues(MatrixMap mat) { + boolean result = true; + // les dimensions doivent-être identique + result = result && MatrixHelper.sameDimension(getDim(), mat.getDim()); + + // toutes les données doivent être identique + for (MatrixMapIterator<E> i = mat.iterator(); result && i.hasNext();) { + E v1 = i.next(); + E v2 = getValueIndex(i.getCoordinates()); + result = v1 == v2; + if (log.isTraceEnabled()) { + log.trace("v1(" + v1 + ")==v2(" + v2 + ")=" + result); + } + } + + return result; + } + + /** + * Si la matrice est 1D + * <pre> + * MaMatrice(matrix1D) [ + * MaDimName: Dim1, Dim2, Dim3, + * v1, v2, v3 + * ] + * </pre> + * + * Si la matrice est 2D + * <pre> + * MaMatrice(matrix2D) [ + * MaDimX + * MaDimY Dim1, Dim2, Dim3, + * DimA v1, v2, v3 + * DimB v4, v5, v6 + * DimC v7, v8, v9 + * ] + * </pre> + * + * Pour les autres types de matrice la methode {@link #toStringGeneric() } + * est utilise + * + * @return + */ + @Override + public String toString() { + int LENGTH = 10; + StringBuilder result = new StringBuilder(); + if (getDimCount() == 1) { + result.append(MatrixHelper.format(getName(), -LENGTH, "#NoNameMat")); + result.append("(matrix1D)[\n"); + String dimName = getDimensionName(0); + result.append(MatrixHelper.format(dimName, LENGTH, "#NoNameDim")); + for (Object sem : getSemantic(0)) { + result.append(","); + result.append(MatrixHelper.format(sem, -LENGTH, null)); + } + result.append(StringUtils.repeat(" ", LENGTH + 1)); + for (int i = 0; i < getDim(0); i++) { + Object v = getValueIndex(i); + result.append(MatrixHelper.format(v, -LENGTH, null) + ","); + } + result.append("\n]"); + } else if (getDimCount() == 2) { + int[] pos = new int[2]; + result.append(MatrixHelper.format(getName(), -LENGTH, "#NoNameMat")); + result.append("(matrix2D) [\n"); + + result.append(StringUtils.repeat(" ", LENGTH + 1)); + String dimNameX = getDimensionName(0); + result.append(MatrixHelper.format(dimNameX, LENGTH, "#DimX")); + result.append("\n"); + String dimNameY = getDimensionName(1); + result.append(MatrixHelper.format(dimNameY, LENGTH, "#DimY")); + result.append(" "); + for (Object sem : getSemantic(0)) { + result.append(MatrixHelper.format(sem, -LENGTH, null)); + result.append(","); + } + + for (int y = 0; y < getDim(1); y++) { + result.append("\n"); + Object sem = getSemantic(1).get(y); + result.append(MatrixHelper.format(sem, LENGTH, null)); + result.append(" "); + for (int x = 0; x < getDim(0); x++) { + pos[0] = x; + pos[1] = y; + Object v = getValueIndex(pos); + result.append(MatrixHelper.format(v, -LENGTH, null) + ","); + } + } + result.append("\n]"); + } else { + result.append(toStringGeneric()); + } + return result.toString(); + } + + /** + * Representation string de la matrice quelque soit le nombre de dimension + * @return + */ + public String toStringGeneric() { + StringBuilder result = new StringBuilder(); + result.append(MatrixHelper.format(getName(), 0, "#NoNameMat")); + result.append("(matrix" + getDimCount() + "D)[\n"); + result.append("dimensions = ["); + for (int i = 0; i < getDim().length; i++) { + result.append(getDim()[i] + ","); + } + result.append("]\ndata = ["); + for (MatrixMapIterator i = this.iterator(); i.hasNext();) { + result.append(i.next() + ","); + } + result.append("]\n"); + return result.toString(); + } + + public boolean isValidCoordinates(int[] dim) { + boolean result = getDimCount() == dim.length; + for (int i = 0; result && i < dim.length; i++) { + result = 0 <= dim[i] && dim[i] < getDim(i); + } + return result; + } + + public boolean isValidCoordinates(Object[] semantics) { + boolean result = getDimCount() == semantics.length; + for (int i = 0; result && i < semantics.length; i++) { + List semantic = getSemantic(i); + result = semantic.contains(semantics[i]); + } + return result; + } + + /** + * Copie une matrice dans la matrice actuelle. La matrice à copier à le même + * nombre de dimension. Si la matrice à copier est trop grande seul les + * éléments pouvant être copier le seront. + * + * @param mat la matrice à copier + * @return return la matrice courante. + */ + public MatrixMap pasteIndex(MatrixMap<E> mat) { + return paste(new int[getDimCount()], mat); + } + + protected MatrixMap<E> paste(int[] origin, MatrixMap<E> mat) { + if (mat != null) { + for (MatrixMapIterator<E> mi = mat.iterator(); mi.hasNext();) { + E value = mi.next(); + int[] coordinates = ArrayUtil.sum(origin, mi.getCoordinates()); + if (isValidCoordinates(coordinates)) { + setValueIndex(value, coordinates); + } + } + } + return this; + } + + /** + * Modifie la matrice actuel en metant les valeurs de mat passé en parametre + * La copie se fait en fonction de la semantique, si un element dans une + * dimension n'est pas trouvé, alors il est passé + */ + public MatrixMap<E> paste(MatrixMap<E> mat) { + if (mat != null) { + for (MatrixMapIterator<E> mi = mat.iterator(); mi.hasNext();) { + E value = mi.next(); + Object[] sems = mi.getSemanticsCoordinates(); + if (isValidCoordinates(sems)) { + setValue(value, sems); + } + } + } + return this; + } + + /** + * Permet de prendre une sous matrice dans la matrice courante. La sous + * matrice a le même nombre de dimensions mais sur une des dimensions on ne + * prend que certain élément. + * + * @param dim la dimension dans lequel on veut une sous matrice si dim est + * négatif alors la dimension est prise à partir de la fin par + * exemple si l'on veut la derniere dimension il faut passer -1 + * pour dim + * @param start la position dans dim d'ou il faut partir pour prendre la + * sous matrice. + * @param nb le nombre d'élément à prendre dans la dimension. si nb est + * inférieur ou égal à 0 alors cela indique qu'il faut prendre + * tous les éléments jusqu'à la fin de la dimension. + * @return new matrix + */ + public MatrixMap<E> getSubMatrix(int dim, int start, int nb) { + if (dim < 0) { + dim = getDimCount() + dim; + } + if (start < 0) { + start = getDim(dim) + start; + } + if (nb <= 0) { + nb = getDim(dim) - start; + } + return new SubMatrix<E>(this, dim, start, nb); + } + + /** + * Permet de prendre une sous matrice dans la matrice courante. La sous + * matrice a le même nombre de dimensions mais sur une des dimensions on ne + * prend que certain élément. + * + * @param dim la dimension dans lequel on veut une sous matrice + * @param start la position dans dim d'ou il faut partir pour prendre la + * sous matrice. 0 <= start < dim.size si start est négatif alors + * la position de départ est calculé par rapport à la fin de la + * dimension, pour avoir le dernier élément il faut passer -1 + * @param nb le nombre d'élément à prendre dans la dimension si nb est + * inférieur ou égal à 0 alors cela indique qu'il faut prendre + * tous les éléments jusqu'à la fin de la dimension. + * @return new matrix + */ + public MatrixMap<E> getSubMatrix(int dim, Object start, int nb) { + int begin = MatrixHelper.indexOf(getSemantics(), dim, start); + return getSubMatrix(dim, begin, nb); + } + + /** + * Add to desambiguas some call with xpath engine, but do the same thing + * {@link #getSubMatrix(int, Object[])} + * + * @param dim + * @param elem + * @return new matrix + */ + public MatrixMap<E> getSubMatrixOnSemantic(int dim, Object... elem) { + MatrixMap<E> result = getSubMatrix(dim, elem); + return result; + } + + /** + * Permet de prendre une sous matrice dans la matrice courante. La sous + * matrice a le même nombre de dimensions mais sur une des dimensions on ne + * prend que certain élément. + * + * @param dim la dimension dans lequel on veut une sous matrice + * @param elem les éléments dans la dimension à conserver + * @return new matrix + */ + public MatrixMap<E> getSubMatrix(int dim, Object... elem) { + int[] ielem = new int[elem.length]; + for (int i = 0; i < ielem.length; i++) { + ielem[i] = MatrixHelper.indexOf(getSemantics(), dim, elem[i]); + } + return getSubMatrix(dim, ielem); + } + + /** + * Permet de prendre une sous matrice dans la matrice courante. + * + * Réalise plusieurs appels à {@link #getSubMatrix(int, Object...)} suivant + * l'implémentation. + * + * @param elem les éléments dans la dimension à conserver + * @return new matrix + */ + public MatrixMap<E> getSubMatrix(Object[]... elems) { + + // la reduction doit se faire sur le meme nombre de dimension + if (elems.length != dim.length) { + throw new IllegalArgumentException(String.format( + "Can't get sub matrix with different dimension count " + + "(expected: %d, got %d)", dim.length, elems.length)); + } + + MatrixMap<E> result = this; + for (int i = 0; i < elems.length; ++i) { + if (elems[i] != null) { + result = result.getSubMatrix(i, elems[i]); + } + } + return result; + } + + /** + * Permet de prendre une sous matrice dans la matrice courante. La sous + * matrice a le même nombre de dimensions mais sur une des dimensions on ne + * prend que certain élément. + * + * @param dim la dimension dans lequel on veut une sous matrice + * @param elem les indices des éléments dans la dimension à conserver + * @return new matrix + */ + public MatrixMap<E> getSubMatrix(int dim, int[] elem) { + return new SubMatrix<E>(this, dim, elem); + } + + /** + * Permet de prendre une sous matrice dans la matrice courante. + * + * Réalise plusieurs appels a {@link #getSubMatrix(int, int[])} suivant + * l'implementation. + * + * @param elems les indices des éléments pour chaque dimension à conserver + * @return new matrix + */ + public MatrixMap<E> getSubMatrix(int[]... elems) { + + // la reduction doit se faire sur le meme nombre de dimension + if (elems.length != dim.length) { + throw new IllegalArgumentException(String.format( + "Can't get sub matrix with different dimension count " + + "(expected: %d, got %d)", dim.length, elems.length)); + } + + MatrixMap<E> result = this; + for (int i = 0; i < elems.length; ++i) { + if (elems[i] != null) { + result = new SubMatrix<E>(result, i, elems[i]); + } + } + return result; + } + + /** + * Reduit la matrice de sorte que toutes les dimensions qui n'ont qu'un + * élement soit supprimée. Au pire cette méthode retourne une matrice à une + * seule dimension à un seul élément. + * + * @return une nouvelle matrice plus petite que la matrice actuelle ou egal + * s'il n'y a aucune dimension à supprimer + */ + public MatrixMap<E> reduce() { + return reduce(1); + } + + /** + * Reduit le matrice seulement sur les dimensions passées en argument. Si + * une des dimensions passées en arguement n'a pas qu'un seul élément, cette + * dimension n'est pas prise en compte. + * + * @param dims les dimensions sur lequel il faut faire la reduction + * @return une nouvelle matrice + */ + public MatrixMap<E> reduceDims(int... dims) { + Arrays.sort(dims); + // tableau permettant de faire la correspondance entre les dimensions + // de la matrice actuelle et les dimentsions de la nouvelle matrice + // l'element i du tableau qui correcpond à la dimensions i de la + // nouvelle matrice contient la dimension equivalente dans + // la matrice actuelle + int[] correspondance = new int[getDimCount()]; + // les nouvelles semantiques + List<List> sem = new ArrayList<List>(); + // les nouveaux noms de dimensions + List<String> dimName = new ArrayList<String>(); + // il faut au moins une dimension pour la matrice + int minNbDim = 1; + for (int j = getDimCount() - 1; j >= 0; j--) { + // si la dimension à plus d'un élément ou qu'il n'est pas dans dims + // on garde la dimension + if (getDim(j) > 1 || Arrays.binarySearch(dims, j) < 0 + || j < minNbDim) { + // on ne conserve que les dimensions supérieure à 1 + correspondance[sem.size()] = j; + sem.add(getSemantic(j)); + dimName.add(getDimensionName(j)); + minNbDim--; + } + } + MatrixMap<E> result = reduce(dimName, sem, correspondance); + return result; + } + + /** + * Reduit la matrice de sorte que toutes les dimensions qui n'ont qu'un + * élement soit supprimée. Au pire cette méthode retourne une matrice à une + * seule dimension à un seul élément. + * + * @param minNbDim le nombre minimum de dimension que l'on souhaite pour la + * matrice résultat + * @return une nouvelle matrice plus petite que la matrice actuelle ou egal + * s'il n'y a aucune dimension à supprimer + */ + public MatrixMap<E> reduce(int minNbDim) { + // tableau permettant de faire la correspondance entre les dimensions + // de la matrice actuelle et les dimentsions de la nouvelle matrice + // l'element i du tableau qui correcpond à la dimensions i de la + // nouvelle matrice contient la dimension equivalente dans + // la matrice actuelle + int[] correspondance = new int[getDimCount()]; + // les nouvelles semantiques + List<List> sem = new ArrayList<List>(); + // les nouveaux noms de dimensions + List<String> dimName = new ArrayList<String>(); + for (int j = getDimCount() - 1; j >= 0; j--) { + // si la dimension à plus d'un élément ou si on a pas assez de + // dimension pour avoir le minimum demandé on prend la dimension + if (getDim(j) > 1 || j < minNbDim) { + // on ne conserve que les dimensions supérieure à 1 + correspondance[sem.size()] = j; + sem.add(getSemantic(j)); + dimName.add(getDimensionName(j)); + // on vient de prendre une dimension il nous en faut une de + // moins + minNbDim--; + } + } + + MatrixMap<E> result = reduce(dimName, sem, correspondance); + return result; + } + + /** + * Create new matrice from the current matrix. + * + * @param dimName dimension name for new matrix + * @param sem semantic for new matrix + * @param correspondance array to do the link between current matrix and + * returned matrix + * @return new matrix + */ + protected MatrixMap<E> reduce(List<String> dimName, List<List> sem, int[] correspondance) { + // on converti les listes en tableau en inversant l'ordre car on + // a fait un parcours en sens inverse + int nbDim = sem.size(); + List[] newSemantics = new List[nbDim]; + String[] newDimNames = new String[nbDim]; + int[] tmpcorrespondance = new int[nbDim]; + for (int i = 0; i < nbDim; i++) { + newSemantics[i] = sem.get(nbDim - 1 - i); + newDimNames[i] = dimName.get(nbDim - 1 - i); + tmpcorrespondance[i] = correspondance[nbDim - 1 - i]; + } + correspondance = tmpcorrespondance; + + MatrixMap<E> result = new MatrixMapFixed<E>(getName(), newDimNames, newSemantics); + + // on reprend les valeurs + int[] newCoordinates = new int[result.getDimCount()]; + for (MatrixMapIterator<E> mi = iterator(); mi.hasNext();) { + E value = mi.next(); + int[] oldCoordinates = mi.getCoordinates(); + for (int i = 0; i < newCoordinates.length; i++) { + newCoordinates[i] = oldCoordinates[correspondance[i]]; + } + result.setValueIndex(value, newCoordinates); + } + return result; + } + + public MatrixMap<E> extend(Object... sems) { + String name = getName(); + String[] dimNames = getDimensionNames(); + SemanticList[] semantics = getSemantics(); + + // si pas assez de dimension on en rajoute + if (sems.length > semantics.length) { + String[] newDimNames = new String[sems.length]; + System.arraycopy(dimNames, 0, newDimNames, 0, dimNames.length); + dimNames = newDimNames; + + SemanticList[] newSems = new SemanticList[sems.length]; + System.arraycopy(semantics, 0, newSems, 0, semantics.length); + semantics = newSems; + + for (int i = semantics.length; i < newSems.length; i++) { + newSems[i] = new SemanticList(); + } + } + + // si les objets demande n'existe pas dans la semantics on l'ajoute + for (int i = 0; i<sems.length; i++) { + if (semantics[i].indexOf(sems[i]) == -1) { + semantics[i].add(sems[i]); + } + } + + MatrixMap<E> result = MatrixMap.Factory.create(name, dimNames, semantics); + result.paste(this); + return result; + } + + } + + /** + * Pour l'instant une sous matrice a obligatoirement le meme nombre de dimension + * que la matrice qu'elle contient. Elle permet juste de reduire le nombre + * d'element d'une dimension. + * + * C'est comme une "vue" réduite sur la vraie matrices. + */ + static public class SubMatrix<E> extends AbstractMatrixMap<E> { // SubMatrix + + protected MatrixMap<E> matrix = null; + protected DimensionConverter converter = null; + + public SubMatrix(MatrixMap<E> matrix, int dim, int start, int nb) { + super(matrix.getName(), matrix.getDimensionNames(), matrix.getSemantics()); + this.matrix = matrix; + + converter = new ShiftConverter(dim, start, nb); + setSemantic(dim, getSemantic(dim).subList(start, start + nb)); + getDim()[dim] = nb; + } + + public SubMatrix(MatrixMap<E> matrix, int dim, int[] elem) { + super(matrix.getName(), matrix.getDimensionNames(), matrix.getSemantics()); + this.matrix = matrix; + + converter = new MappingConverter(dim, elem); + + List oldSemantic = getSemantic(dim); + List newSemantic = new LinkedList(); + for (int i = 0; i < elem.length; i++) { + newSemantic.add(oldSemantic.get(elem[i])); + } + setSemantic(dim, newSemantic); + getDim()[dim] = elem.length; + } + + @Override + public MatrixMapIterator<E> iterator() { + return new SubMatrixIterator<E>(this); + } + + @Override + public E getValueIndex(int ... coordinates) { + return matrix.getValueIndex(converter.convertCoordinates(coordinates)); + } + + @Override + public void setValueIndex(E value, int ... coordinates) { + matrix.setValueIndex(value, converter.convertCoordinates(coordinates)); + } + + protected class SubMatrixIterator<E> implements MatrixMapIterator<E> { + + protected SubMatrix<E> subMatrix = null; + protected int[] cpt = null; + protected int[] last = null; + + public SubMatrixIterator(SubMatrix<E> subMatrix) { + this.subMatrix = subMatrix; + cpt = new int[subMatrix.getDimCount()]; + cpt[cpt.length - 1] = -1; + + last = new int[subMatrix.getDimCount()]; + for (int i = 0; i < last.length; i++) { + last[i] = subMatrix.getDim(i) - 1; + } + + } + + @Override + public boolean hasNext() { + return !Arrays.equals(cpt, last); + } + + @Override + public E next() { + int ret = 1; + int[] dim = getDim(); + for (int i = cpt.length - 1; i >= 0; i--) { + cpt[i] = cpt[i] + ret; + ret = cpt[i] / dim[i]; + cpt[i] = cpt[i] % dim[i]; + } + E result = getValue(); + return result; + } + + @Override + public void remove() { + setValue(null); + } + + public int[] getCoordinates() { + return cpt; + } + + public Object[] getSemanticsCoordinates() { + int[] coordinates = getCoordinates(); + Object[] result = MatrixHelper.dimensionToSemantics(subMatrix.getSemantics(), coordinates); + return result; + } + + public E getValue() { + return subMatrix.getValueIndex(getCoordinates()); + } + + public void setValue(E value) { + subMatrix.setValue(value, getCoordinates()); + } + } + + /** + * Permet de faire une conversion de la dimension demandé dans la sous + * matrice avec la position reel de la matrice sous jacente. + */ + protected interface DimensionConverter extends Serializable { + public int[] convertCoordinates(int[] coordinates); + } + + /** + * La conversion est juste un decalage d'indice + */ + protected static class ShiftConverter implements DimensionConverter { + + /** serialVersionUID. */ + private static final long serialVersionUID = 1L; + + protected int dim; + protected int start; + protected int nb; + + public ShiftConverter(int dim, int start, int nb) { + this.dim = dim; + this.start = start; + this.nb = nb; + } + + @Override + public int[] convertCoordinates(int[] coordinates) { + int[] result = null; + if (coordinates[dim] < nb) { + result = new int[coordinates.length]; + System.arraycopy(coordinates, 0, result, 0, result.length); + result[dim] = result[dim] + start; + } else { + throw new NoSuchElementException( + "L'indice est supérieur au nombre d'élement de la sous matrice pour cette dimension."); + } + return result; + } + } + + /** + * La conversion est le mapping d'un element vers un autre element. + */ + protected static class MappingConverter implements DimensionConverter { + + /** serialVersionUID. */ + private static final long serialVersionUID = -6367416559713556559L; + protected int dim; + protected int[] elem = null; + + public MappingConverter(int dim, int[] elem) { + this.dim = dim; + this.elem = new int[elem.length]; + System.arraycopy(elem, 0, this.elem, 0, elem.length); + } + + @Override + public int[] convertCoordinates(int[] coordinates) { + int[] result = null; + if (coordinates[dim] < elem.length) { + result = new int[coordinates.length]; + System.arraycopy(coordinates, 0, result, 0, result.length); + result[dim] = elem[coordinates[dim]]; + + } else { + throw new NoSuchElementException( + "L'indice est supérieur au nombre d'élements de la sous matrice pour cette dimension."); + } + return result; + } + } + } // SubMatrix + + /** + * Objet matrice qui ne permet que le stockage avec des positions int + * dans une matrice a autant de dimension que l'on souhaite. + */ + static public class Matrix<E> implements Iterable<E> { // BasicMatrix + + /** Les dimensions de la matrice */ + protected int[] dimensions = null; + /** La matrice en représentation linéaire */ + protected Vector<E> data = null; + + /** + * tableau de facteur permettant de convertir les coordonnées dans la + * matrice en un indice dans la représentation linéaire de la matrice + */ + protected int[] linearFactor = null; + + /** + * Crée une nouvelle matrice ayant les dimensions demandées. + * + * @param factory factory + * @param dimensions dimensions + */ + public Matrix(int[] dimensions) { + checkDim(dimensions); + + // copie des dimensions pour que personne à l'extérieur de l'objet + // ne puisse les modifiers par la suite + this.dimensions = new int[dimensions.length]; + System.arraycopy(dimensions, 0, this.dimensions, 0, dimensions.length); + + // calcul du linearFactor + linearFactor = new int[dimensions.length]; + linearFactor[linearFactor.length - 1] = 1; + for (int i = linearFactor.length - 2; i >= 0; i--) { + linearFactor[i] = linearFactor[i + 1] * dimensions[i + 1]; + } + + // creation de la matrice lineaire + data = new Vector<E>(linearFactor[0] * dimensions[0]); + } + + /** + * Retourne le nombre de dimension de la matrice + * + * @return le nombre de dimension de la matrice; + */ + public int getNbDim() { + return dimensions.length; + } + + /** + * Retourne la taille d'une dimension + * + * @param dim la dimension dont on souhaite la taille + * @return la taille d'une dimension + */ + public int getDim(int dim) { + checkDim(dim); + return dimensions[dim]; + } + + /** + * Retourne un tableau representant les dimensions de la matrice. Le tableau + * retourné n'est pas une copie, il ne faut donc pas le modifier + * + * @return le tableau des dimensions. + */ + public int[] getDim() { + return dimensions; + } + + /** + * Retourne un element de la matrice + * + * @param pos la position de l'element à retourner + * @return un element de la matrice + */ + public E getValue(int[] pos) { + int indice = coordonatesToLinear(pos); + return data.getValue(indice); + } + + /** + * Modifie un élement de la matrice + * + * @param pos la position de l'element à modifier + * @param value la nouvelle valeur à mettre dans la matrice + */ + public void setValue(int[] pos, E value) { + int indice = coordonatesToLinear(pos); + data.setValue(indice, value); + } + + /** + * Retourne un objet Inc pret a etre utilisé pour boucler sur tous les + * element de la matrice. + * + * @return un objet Inc pret à être utilisé + */ + public MatrixIterator<E> iterator() { + return new MatrixIterator<E>(this); + } + + /** + * Permet de faire un traitement sur chaque valeur de la matrice + * + * @param f la fonction a appliquer à chaque élement de la matrice + */ + public void map(MapFunction f) { + data.map(f); + } + + /** + * Permet de convertir les coordonnées d'un élément en un indice dans la + * représentation linéraire de la matrice. + * + * @param coordonates les coordonnées à lineariser + * @return un indice réprésentant les coordonnées de façon linéaire + */ + protected int coordonatesToLinear(int[] coordonates) { + checkPos(coordonates); + + int result = 0; + for (int i = 0; i < linearFactor.length; i++) { + result += coordonates[i] * linearFactor[i]; + } + return result; + } + + /** + * Convertie une coordonnée lineaire en coordonnées spaciales + * + * @param pos la coordonnée linéaire + * @return les coordonnées spaciales de l'élément + */ + protected int[] linearToCoordinates(int pos) { + int[] result = new int[linearFactor.length]; + + for (int i = 0; i < result.length; i++) { + result[i] = pos / linearFactor[i]; + pos -= result[i] * linearFactor[i]; + } + return result; + } + + /** + * Permet de vérifier que les dimensions de la nouvelle matrice sont + * corrects + * + * @param dim les dimensions de la nouvelle matrice + * @throws IllegalArgumentException si une dimension n'est pas valide + */ + protected void checkDim(int[] dim) { + for (int i = 0; i < dim.length; i++) { + if (dim[i] <= 0) { + throw new IllegalArgumentException(String.format( + "Dimension %s is invalid %s", i, dim[i])); + } + } + } + + /** + * Permet de vérifier qu'une dimension demandé existe bien dans la matrice + * + * @param dim la position de la dimension que l'on souhaite + * @throws IndexOutOfBoundsException si la dimension demandée n'existe pas + */ + protected void checkDim(int dim) { + if (dim < 0 || dim >= getNbDim()) { + throw new IndexOutOfBoundsException(String.format( + "Invalid dimension %s max dimension is %s", + dim, getNbDim())); + } + } + + /** + * Verifie que les coordonnées demandé appartiennent bien à la matrice + * + * @param pos les coordonnées souhaitées dans la matrice + * @throws NoSuchElementException si les coordonnées ne correspondent pas à + * un élement de la matrice + */ + protected void checkPos(int[] pos) { + int[] dim = getDim(); + boolean result = dim.length == pos.length; + for (int i = 0; result && i < dim.length; i++) { + result = (0 <= pos[i]) && (pos[i] < dim[i]); + } + if (!result) { + throw new NoSuchElementException(String.format( + "Invalid element asked %s for real dimension %s", Arrays.toString(pos), Arrays + .toString(dim))); + } + } + + @Override + public String toString() { + StringBuffer result = new StringBuffer(); + if (getNbDim() == 1) { + result.append("matrix1D ["); + for (int i = 0; i < data.size(); i++) { + result.append(data.getValue(i) + ","); + } + result.append("]"); + } else if (getNbDim() == 2) { + int[] pos = new int[2]; + result.append("matrix2D ["); + for (int y = 0; y < getDim(1); y++) { + result.append("\n"); + for (int x = 0; x < getDim(0); x++) { + pos[0] = x; + pos[1] = y; + result.append(getValue(pos) + ","); + } + } + result.append("]"); + } else { + result.append("dimensions = [\n"); + for (int i = 0; i < dimensions.length; i++) { + result.append(dimensions[i] + ","); + } + result.append("\n]\nmatrice = [\n"); + for (int i = 0; i < data.size(); i++) { + result.append(data.getValue(i) + ","); + } + result.append("\n]\nlinearFactor = [\n"); + for (int i = 0; i < linearFactor.length; i++) { + result.append(linearFactor[i] + ","); + } + result.append("\n]\n"); + } + return result.toString(); + } + + @Override + public boolean equals(Object o) { + if (o instanceof Matrix) { + Matrix other = (Matrix) o; + return this == o + || (Arrays.equals(this.dimensions, other.dimensions) && this.data + .equals(other.data)); + } + return false; + } + + } // BasicMatrix + + static public class MatrixIterator<E> implements Iterator<E> { // MatrixIteratorImpl + + protected Matrix<E> matrix = null; + protected int pos = -1; + + /** + * @param matrix la matrice sur lequel l'iterator doit travailler + */ + public MatrixIterator(Matrix<E> matrix) { + this.matrix = matrix; + pos = -1; + } + + @Override + public boolean hasNext() { + return pos + 1 < matrix.data.size(); + } + + @Override + public E next() { + if (hasNext()) { + pos++; + } else { + throw new NoSuchElementException(); + } + E result = getValue(); + return result; + } + + @Override + public void remove() { + setValue(null); + } + + public E getValue() { + return matrix.data.getValue(pos); + } + + public void setValue(E value) { + matrix.data.setValue(pos, value); + } + + public int[] getCoordinates() { + return matrix.linearToCoordinates(pos); + } + + } // MatrixIteratorImpl + + /** + * Permet de stocker des données à une position lineaire et de la redemander. + * Cette classe ne gére que les données lineaire. L'avantage de cette classe est + * de ne conserver que les elements differents de la valeur par defaut, ce qui + * minimize la taille du tableau necessaire a conserver les données. + * + */ + static public class Vector<E> { // Vector + + /** maximum number of element, maximum pos value */ + protected int capacity = 0; + + /** la valeur par defaut */ + protected E defaultValue = null; + + /** contient la position de l'element, le tableau est trie */ + protected int[] position; + protected int positionSize = 0; + + /** contient la valeur de l'element */ + protected ArrayList<E> data = new ArrayList<E>(); + + public Vector(int capacity) { + this.capacity = capacity; + position = new int[8]; + Arrays.fill(position, Integer.MAX_VALUE); + } + + public Vector(int capacity, E defaultValue) { + this(capacity); + this.defaultValue = defaultValue; + } + + public int size() { + return capacity; + } + + // poussin 20060827 TODO: verifier l'implantation, il semble quelle soit + // fausse et ne puisse pas recherche le nombre max correctement + public E getMaxOccurrence() { + E result = defaultValue; + + E[] tmp = (E[])data.toArray(); + + // si potentiellement il y a plus d'element identique dans data + // que de valeur par defaut, on recherche la valeur possible + if (this.capacity < 2 * tmp.length) { + Arrays.sort(tmp); + + // le nombre de fois que l'on a rencontrer la valeur la plus + // nombreuse + int max = 1; + // le nombre de fois que l'on a rencontrer la valeur courante + int count = 1; + // la valeur la plus rencontrer + result = tmp[0]; + // la valeur que l'on vient de traiter précédement + E old = tmp[0]; + // la valeur courante lu dans le tableaux + E current = tmp[0]; + // tant que l'on peut encore trouve un element plus nombreux dans le + // tableau on le parcours + for (int i = 1; max < tmp.length - i + count && i < tmp.length; i++) { + current = tmp[i]; + + if (current == old) { + count++; + } else { + if (count > max) { + max = count; + result = old; + } + count = 1; + old = current; + } + } + if (count > max) { + max = count; + result = current; + } + + if (max <= capacity - tmp.length) { + // en fin de compte, il n'y a pas plus d'element identique + // dans data que de defaultValue + result = defaultValue; + } + } + + return result; + } + + protected void checkPos(int pos) { + if (pos < 0 || pos >= capacity) { + throw new IllegalArgumentException("pos " + pos + " is not in [0, " + + capacity + "]"); + } + } + + public E getValue(int pos) { + checkPos(pos); + + E result = defaultValue; + int index = findIndex(pos); + if (index >= 0) { + result = data.get(index); + } + return result; + } + + public void setValue(int pos, E value) { + checkPos(pos); + + int index = findIndex(pos); + if (index >= 0) { + if (value == defaultValue) { + // il etait present, on supprime l'element + removeElementAt(index); + data.remove(index); + } else { + // il etait deja present, on modifie la valeur + data.set(index, value); + } + } else { + // il n'etait pas present + if (value != defaultValue) { + // il faut ajouter dans position et dans data + index = -index - 1; + + addElementAt(index, pos); + data.add(index, value); + } + } + } + + public boolean equals(Object o) { + boolean result = false; + if (o instanceof Vector) { + Vector other = (Vector) o; + result = Arrays.equals(this.position, other.position) + && data.equals(other.data); + } + return result; + } + + /** + * retourne la position dans le tableau position de la position lineaire + * + * @param pos + * @return la position ou < 0 donnant la position de l'element s'il etait + * present + */ + protected int findIndex(int pos) { + return Arrays.binarySearch(position, pos); + } + + protected void ensureCapacity(int mincap) { + if (mincap > position.length) { + int newcap = (position.length * 3) / 2 + 1; + int olddata[] = position; + position = new int[newcap >= mincap ? newcap : mincap]; + System.arraycopy(olddata, 0, position, 0, positionSize); + for (int i = positionSize; i < position.length; i++) { + position[i] = Integer.MAX_VALUE; + } + } + } + + protected void addElementAt(int index, int element) { + ensureCapacity(positionSize + 1); + int numtomove = positionSize - index; + System.arraycopy(position, index, position, index + 1, numtomove); + position[index] = element; + positionSize++; + } + + protected int removeElementAt(int index) { + int oldval = position[index]; + int numtomove = positionSize - index - 1; + if (numtomove > 0) { + System.arraycopy(position, index + 1, position, index, numtomove); + } + positionSize--; + position[positionSize] = Integer.MAX_VALUE; + return oldval; + } + + /** + * On recopie tous les attributs pour que le vector ressemble exactement a + * celui passé en argument + */ + public void paste(Vector<E> v) { + this.capacity = v.capacity; + this.defaultValue = v.defaultValue; + this.positionSize = v.positionSize; + this.position = new int[v.position.length]; + System.arraycopy(v.position, 0, this.position, 0, + this.position.length); + this.data.clear(); + this.data.addAll(v.data); + } + + /** + * on applique sur chaque donnée existante et sur default + */ + public void map(MapFunction<E> f) { + // on commence toujours par modifier la valeur par defaut + // car les valeurs suivante pourrait prendre cette valeur + // et donc disparaitre des tableaux si besoin + defaultValue = f.apply(defaultValue); + // on fait la boucle a l'envers au cas ou on supprime des valeurs + for (int i = data.size() - 1; i >= 0; i--) { + E value = f.apply(data.get(i)); + if (value == defaultValue) { + // il etait present, on supprime l'element + removeElementAt(i); + data.remove(i); + } else { + // il etait deja present, on modifie la valeur + data.set(i, value); + } + } + } + } // Vector + + /** + * Permet de faire un traitement sur des valeurs et d'en retourner + * des nouvelles. + */ + static public interface MapFunction<E> { // MapFunction + + /** + * Permet de faire un traitement sur value et de retourne une nouvelle + * valeur. + * + * @param value la valeur courante sur lequel il faut faire le traitement + * @return la nouvelle valeur à mettre dans la matrice à la place de + * l'ancienne. + */ + E apply(E value); + + } // MapFunction + +} Added: trunk/nuiton-utils/src/test/java/org/nuiton/util/MatrixMapTest.java =================================================================== --- trunk/nuiton-utils/src/test/java/org/nuiton/util/MatrixMapTest.java (rev 0) +++ trunk/nuiton-utils/src/test/java/org/nuiton/util/MatrixMapTest.java 2011-06-21 11:00:42 UTC (rev 2151) @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2011 poussin. All rights reserved. + * + * 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 <http://www.gnu.org/licenses/>. + */ +package org.nuiton.util; + +import java.util.Arrays; +import java.util.List; +import org.junit.Assert; +import org.junit.Test; +import static org.junit.Assert.*; + +/** + * + * @author poussin + */ +public class MatrixMapTest { + + @Test + public void testSubMatrix() { + List<String> sem1 = Arrays.asList("xA", "xB", "xC", "xD"); + List<String> sem2 = Arrays.asList("ya", "yb", "yc", "yd", "ye"); + MatrixMap<String> m = MatrixMap.Factory.create(new List[]{sem1, sem2}); + + m.setValue("a1", "xA", "ya"); + m.setValue("a2", "xA", "yb"); + m.setValue("a3", "xA", "yc"); + m.setValue("a4", "xA", "yd"); + m.setValue("a5", "xA", "ye"); + + m.setValue("b1", "xB", "ya"); + m.setValue("b2", "xB", "yb"); + m.setValue("b3", "xB", "yc"); + m.setValue("b4", "xB", "yd"); + m.setValue("b5", "xB", "ye"); + + m.setValue("c1", "xC", "ya"); + m.setValue("c2", "xC", "yb"); + m.setValue("c3", "xC", "yc"); + m.setValue("c4", "xC", "yd"); + m.setValue("c5", "xC", "ye"); + + m.setValue("d1", "xD", "ya"); + m.setValue("d2", "xD", "yb"); + m.setValue("d3", "xD", "yc"); + m.setValue("d4", "xD", "yd"); + m.setValue("d5", "xD", "ye"); + + System.out.println(m.toString()); + + MatrixMap sub = m.getSubMatrix(0, "xA", "xC"); + + System.out.println(sub); + } + + @Test + public void testExtend() { + List<String> sem1 = Arrays.asList("xA", "xB", "xC"); + List<String> sem2 = Arrays.asList("ya", "yb", "yc", "yd", "ye"); + MatrixMap<String> m = MatrixMap.Factory.createElastic(new List[]{sem1, sem2}); + + m.setValue("a1", "xA", "ya"); + m.setValue("a2", "xA", "yb"); + m.setValue("a3", "xA", "yc"); + m.setValue("a4", "xA", "yd"); + m.setValue("a5", "xA", "ye"); + + m.setValue("b1", "xB", "ya"); + m.setValue("b2", "xB", "yb"); + m.setValue("b3", "xB", "yc"); + m.setValue("b4", "xB", "yd"); + m.setValue("b5", "xB", "ye"); + + m.setValue("c1", "xC", "ya"); + m.setValue("c2", "xC", "yb"); + m.setValue("c3", "xC", "yc"); + m.setValue("c4", "xC", "yd"); + m.setValue("c5", "xC", "ye"); + + System.out.println(m.toString()); + + m.setValue("d1", "xD", "ya"); + m.setValue("d2", "xD", "yb"); + m.setValue("d3", "xD", "yc"); + m.setValue("d4", "xD", "yd"); + m.setValue("d5", "xD", "ye"); + + System.out.println(m.toString()); + } + +}
participants (1)
-
bpoussin@users.nuiton.org