
org.nuiton.util.MatrixMap Maven / Gradle / Ivy
Show all versions of nuiton-utils Show documentation
/*
* #%L
* Nuiton Utils
* %%
* Copyright (C) 2004 - 2012 CodeLutin, Chatellier Eric
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser 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 Lesser Public License for more details.
*
* You should have received a copy of the GNU General Lesser Public
* License along with this program. If not, see
* .
* #L%
*/
package org.nuiton.util;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
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;
/**
* 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.
*
* 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.
*
* Pour créer une nouvelle matrice, il faut utiliser une des méthodes de
* {@link MatrixMap.Factory}
*
* @author Benjamin Poussin - [email protected]
* @since 2.2.1
*/
public interface MatrixMap extends Iterable {
/**
* Classe permettant la creation de matrice
*/
class Factory {
public static MatrixMap create(List... semantics) {
MatrixMap result = new MatrixMapFixed(semantics);
return result;
}
public static MatrixMap create(String name, List... semantics) {
MatrixMap result = new MatrixMapFixed(name, semantics);
return result;
}
public static MatrixMap create(String name, String[] dimNames, List... semantics) {
MatrixMap result = new MatrixMapFixed(name, dimNames, semantics);
return result;
}
public static MatrixMap create(MatrixMap matrix) {
MatrixMap result = new MatrixMapFixed(matrix);
return result;
}
public static MatrixMap createElastic(List... semantics) {
MatrixMap result = create(semantics);
result = createElastic(result);
return result;
}
public static MatrixMap createElastic(String name, List... semantics) {
MatrixMap result = create(name, semantics);
result = createElastic(result);
return result;
}
public static MatrixMap createElastic(String name, String[] dimNames, List... semantics) {
MatrixMap result = create(name, dimNames, semantics);
result = createElastic(result);
return result;
}
public static MatrixMap createElastic(MatrixMap matrix) {
MatrixMap result = new MatrixMapElastic(matrix);
return result;
}
}
@Override
MatrixMapIterator iterator();
/**
* Copy la matrice pour pouvoir la modifier sans perdre les donnees
* initiales.
*
* @return new matrix
*/
MatrixMap copy();
SemanticList[] getSemantics();
SemanticList getSemantic(int dim);
void setSemantic(int dim, List sem);
void setName(String name);
String getName();
String[] getDimensionNames();
void setDimensionNames(String[] names);
void setDimensionName(int dim, String name);
String getDimensionName(int dim);
int getDimCount();
int[] getDim();
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
*/
MatrixMap map(MapFunction f);
/**
* Retourne l'element a une certaine position en utilisant des indices
* ex: 2,3,1
*
* @param coordinates FIXME
* @return FIXME
*/
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 FIXME
*/
void setValueIndex(E value, int... coordinates);
/**
* Retourne l'element a une certaine position en utilisant les semantiques
*
* @param coordinates FIXME
* @return FIXME
*/
E getValue(Object... coordinates);
/**
* Modifie l'element a une certaine position en utilisant les semantiques
*
* @param value la nouvelle valeur
* @param coordinates FIXME
*/
void setValue(E value, Object... coordinates);
/**
* Verifie que deux matrices sont completement equals
* (dimension, semantique, nom, valeur, ...)
*
* @param mat FIXME
* @return FIXME
*/
boolean equals(MatrixMap mat);
/**
* Verifie si les matrices sont egales en ne regardant que les valeurs et
* pas les semantiques
*
* @param mat FIXME
* @return equality on values
*/
boolean equalsValues(MatrixMap mat);
/**
* Representation string de la matrice quelque soit le nombre de dimension
*
* @return FIXME
*/
String toStringGeneric();
/**
* Indique si les semantiques passées en argument sont valable pour la
* matrice courante
*
* @param semantics FIXME
* @return FIXME
*/
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.
*/
MatrixMap paste(MatrixMap 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
*/
MatrixMap 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
*/
MatrixMap 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 elems les éléments dans la dimension à conserver
* @return new matrix
*/
MatrixMap 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
*/
MatrixMap 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
*/
MatrixMap 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
*/
MatrixMap 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 FIXME
* @return FIXME
*/
MatrixMap 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
*/
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 FIXME
*/
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 FIXME
*/
interface MatrixMapIterator extends Iterator {
int[] getCoordinates();
E getValue();
void setValue(E value);
Object[] getSemanticsCoordinates();
}
class MatrixMapIteratorImpl implements MatrixMapIterator { // MatrixMapIteratorImpl
protected MatrixIterator 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 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.
*
* 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 FIXME
*/
class SemanticList extends AbstractList implements RandomAccess {
protected ArrayList datas = null;
protected Map index = new HashMap();
public SemanticList() {
this(new ArrayList());
}
public SemanticList(Collection c) {
datas = new ArrayList(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 index = getIndex();
Integer result = index.get(o);
int resultIndex = -1;
if (result != null) {
resultIndex = result.intValue();
}
return resultIndex;
}
protected Map 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.
*
* 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)
*
* 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.
*
* 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 FIXME
*/
class MatrixMapElastic implements MatrixMap {
protected MatrixMap internalMatrixMap;
public MatrixMapElastic() {
internalMatrixMap = Factory.create();
}
public MatrixMapElastic(MatrixMap m) {
setInternalMatrixMap(m);
}
public MatrixMap getInternalMatrixMap() {
return internalMatrixMap;
}
public void setInternalMatrixMap(MatrixMap internalMatrixMap) {
this.internalMatrixMap = internalMatrixMap;
}
public MatrixMapIterator iterator() {
return getInternalMatrixMap().iterator();
}
public MatrixMap 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 map(MapFunction 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 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 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 mat) {
return getInternalMatrixMap().paste(mat);
}
public MatrixMap getSubMatrix(int dim, Object start, int nb) {
return getInternalMatrixMap().getSubMatrix(dim, start, nb);
}
public MatrixMap getSubMatrix(int dim, Object... elem) {
return getInternalMatrixMap().getSubMatrix(dim, elem);
}
public MatrixMap getSubMatrix(Object[]... elems) {
return getInternalMatrixMap().getSubMatrix(elems);
}
public MatrixMap reduce() {
return getInternalMatrixMap().reduce();
}
public MatrixMap reduceDims(int... dims) {
return getInternalMatrixMap().reduceDims(dims);
}
public MatrixMap reduce(int minNbDim) {
return getInternalMatrixMap().reduce(minNbDim);
}
public MatrixMap 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
*
* @param FIXME
*/
class MatrixMapFixed extends AbstractMatrixMap {
/**
* Logger.
*/
private static final Log log = LogFactory.getLog(MatrixMapFixed.class);
protected Matrix 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 matrix) {
this(matrix.getName(), matrix.getDimensionNames(), matrix.getSemantics());
this.pasteIndex(matrix);
}
protected Matrix getMatrix() {
if (matrix == null) {
matrix = new Matrix(getDim());
}
return matrix;
}
@Override
public MatrixMapIterator iterator() {
return new MatrixMapIteratorImpl(getMatrix().iterator(), getSemantics());
}
@Override
public MatrixMap map(MapFunction 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.
*
* Exemple: Si on a un matrice 3D.
* m.set(v, [1,1,1]) modifie un element de la matrice.
*
* @param value la value a inserer
* @param coordinates les coordonées où faire le remplacement
*/
@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 paste(int[] origin, MatrixMap 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) mat).getMatrix().data);
} else {
super.paste(origin, mat);
}
}
return this;
}
}
/**
* Classe abstraite permettant de facilement implanter les matrice fixe,
* elastique et submatrix
*
* @param FIXME
*/
abstract class AbstractMatrixMap implements MatrixMap {
/**
* Logger.
*/
private static final 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 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 copy() {
MatrixMap result = new MatrixMapFixed(this);
return result;
}
@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 map(MapFunction f) {
for (MatrixMapIterator 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 FIXME
* @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 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
*
* MaMatrice(matrix1D) [
* MaDimName: Dim1, Dim2, Dim3,
* v1, v2, v3
* ]
*
*
* Si la matrice est 2D
*
* MaMatrice(matrix2D) [
* MaDimX
* MaDimY Dim1, Dim2, Dim3,
* DimA v1, v2, v3
* DimB v4, v5, v6
* DimC v7, v8, v9
* ]
*
*
* Pour les autres types de matrice la methode {@link #toStringGeneric() }
* est utilise
*
* @return FIXME
*/
@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 FIXME
*/
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 mat) {
return paste(new int[getDimCount()], mat);
}
protected MatrixMap paste(int[] origin, MatrixMap mat) {
if (mat != null) {
for (MatrixMapIterator 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 paste(MatrixMap mat) {
if (mat != null) {
for (MatrixMapIterator 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 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(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 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 FIXME
* @param elem FIXME
* @return new matrix
*/
public MatrixMap getSubMatrixOnSemantic(int dim, Object... elem) {
MatrixMap 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 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 elems les éléments dans la dimension à conserver
* @return new matrix
*/
public MatrixMap 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 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 getSubMatrix(int dim, int[] elem) {
return new SubMatrix(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 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 result = this;
for (int i = 0; i < elems.length; ++i) {
if (elems[i] != null) {
result = new SubMatrix(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 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 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 sem = new ArrayList();
// les nouveaux noms de dimensions
List dimName = new ArrayList();
// 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 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 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 sem = new ArrayList();
// les nouveaux noms de dimensions
List dimName = new ArrayList();
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 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 reduce(List dimName, 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 result = new MatrixMapFixed(getName(), newDimNames, newSemantics);
// on reprend les valeurs
int[] newCoordinates = new int[result.getDimCount()];
for (MatrixMapIterator 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 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 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.
*/
class SubMatrix extends AbstractMatrixMap { // SubMatrix
protected MatrixMap matrix = null;
protected DimensionConverter converter = null;
public SubMatrix(MatrixMap 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 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 iterator() {
return new SubMatrixIterator(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 implements MatrixMapIterator {
protected SubMatrix subMatrix = null;
protected int[] cpt = null;
protected int[] last = null;
public SubMatrixIterator(SubMatrix 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 {
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.
*
* @param FIXME
*/
class Matrix implements Iterable { // BasicMatrix
/**
* Les dimensions de la matrice
*/
protected int[] dimensions = null;
/**
* La matrice en représentation linéaire
*/
protected Vector 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 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(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é
*/
@Override
public MatrixIterator iterator() {
return new MatrixIterator(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
class MatrixIterator implements Iterator { // MatrixIteratorImpl
protected Matrix matrix = null;
protected int pos = -1;
/**
* @param matrix la matrice sur lequel l'iterator doit travailler
*/
public MatrixIterator(Matrix 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.
*
* @param FIXME
*/
class Vector { // 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 data = new ArrayList();
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 FIXME
* @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
* @param v FIXME
*/
public void paste(Vector 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
* @param f FIXME
*/
public void map(MapFunction 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.
*
* @param FIXME
*/
interface MapFunction { // 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
}