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

org.nuiton.math.matrix.AbstractMatrixND Maven / Gradle / Ivy

The newest version!
/* *##% NuitonMatrix
 * Copyright (C) 2004 - 2009 CodeLutin
 *
 * 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
 * . ##%*/

package org.nuiton.math.matrix;

import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.regex.Pattern;

import org.apache.commons.collections.primitives.ArrayIntList;
import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.nuiton.util.ArrayUtil;

/**
 * AbstractMatrixND.
 *
 * Created: 29 oct. 2004
 *
 * @author Benjamin Poussin 
 * @version $Revision: 199 $
 *
 * Mise a jour: $Date: 2009-11-04 15:22:06 +0100 (mer., 04 nov. 2009) $
 * par : $Author: echatellier $
 */
public abstract class AbstractMatrixND implements MatrixND { // AbstractMatrixND

    /** serialVersionUID. */
    private static final long serialVersionUID = -6838751468730930727L;

    /** to use log facility, just put in your code: log.info(\"...\"); */
    private static Log log = LogFactory.getLog(AbstractMatrixND.class);

    @Override
    public abstract MatrixIterator iterator();

    @Override
    public abstract double getValue(int[] coordinates);

    @Override
    public abstract void setValue(int[] coordinates, double d);

    protected transient DimensionHelper dimHelper = new DimensionHelper();

    protected transient MatrixFactory factory = null;

    protected String name = "";

    protected String[] dimNames = null;

    protected int[] dim = null;

    protected List[] semantics = null;

    protected double defaultValue = 0;

    /**
     * Separateur CSV par défaut le point virgule.
     */
    public static final char CSV_SEPARATOR = ';';

    protected static final Pattern NUMBER = Pattern
            .compile(" *[+-]?[0-9]*\\.?[0-9]+([eE][+-]?[0-9]+)? *");

    protected void init(int[] dim) {
        this.dim = new int[dim.length];
        System.arraycopy(dim, 0, this.dim, 0, dim.length);
        semantics = new List[dim.length];
        dimNames = new String[dim.length];
        // par defaut chaine vide pour le nom des dimensions
        Arrays.fill(dimNames, "");
    }

    protected AbstractMatrixND(MatrixFactory factory) {
        this.factory = factory;
    }

    public AbstractMatrixND(MatrixFactory factory, int[] dim) {
        this(factory);
        init(dim);
        for (int i = 0; i < getDimCount(); i++) {
            // par defaut les listes des semantiques contiennent des nulls
            semantics[i] = Collections.nCopies(dim[i], null);
        }
    }

    public AbstractMatrixND(MatrixFactory factory, List[] semantics) {
        this(factory);
        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]);
        }
    }

    public AbstractMatrixND(MatrixFactory factory, String name, int[] dim) {
        this(factory, dim);
        setName(name);
    }

    public AbstractMatrixND(MatrixFactory factory, String name, int[] dim,
            String[] dimNames) {
        this(factory, dim);
        setName(name);
        for (int i = 0; dimNames != null && i < dimNames.length; i++) {
            setDimensionName(i, dimNames[i]);
        }
    }

    public AbstractMatrixND(MatrixFactory factory, String name,
            List[] semantics) {
        this(factory, semantics);
        setName(name);
    }

    public AbstractMatrixND(MatrixFactory factory, String name,
            List[] semantics, String[] dimNames) {
        this(factory, name, semantics);
        for (int i = 0; dimNames != null && i < dimNames.length; i++) {
            setDimensionName(i, dimNames[i]);
        }
    }

    /*
     * @see org.nuiton.math.matrix.MatrixND#copy()
     */
    @Override
    public MatrixND copy() {
        MatrixND result = getFactory().create(this);
        return result;
    }

    /*
     * @see java.lang.Object#clone()
     */
    @Override
    public MatrixND clone() {
        return copy();
    }

    /**
     * Retourne la factory utilisée pour créer cette matrice, la factory
     * peut-être réutilisé pour créer d'autre matrice si besoin.
     */
    @Override
    public MatrixFactory getFactory() {
        return factory;
    }

    @Override
    public List[] getSemantics() {
        return semantics;
    }

    /**
     * {@inheritDoc}
     *
     * @deprecated Use #getSemantics(dim)
     */
    @Deprecated
    @Override
    public List getSemantics(int dim) {
        return getSemantic(dim);
    }

    @Override
    public List getSemantic(int dim) {
        return semantics[dim];
    }

    /**
     * {@inheritDoc}
     *
     * @deprecated Use #setSemantic(dim, List)
     */
    @Deprecated
    @Override
    public  void setSemantics(int dim, List sem) {
        setSemantic(dim, sem);
    }

    @Override
    public  void setSemantic(int dim, List sem) {
        if (!(sem instanceof SemanticList)) {
            sem = new SemanticList(sem);
        }
        // else SemanticList is immutable and can be used in many matrix in
        // same time this permit to used same indexOf optimization
        semantics[dim] = sem;
    }

    @Override
    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public String[] getDimensionNames() {
        return dimNames;
    }

    @Override
    public void setDimensionNames(String[] names) {
        for (int i = 0; names != null && i < names.length; i++) {
            setDimensionName(i, names[i]);
        }
    }

    /**
     * {@inheritDoc}
     *
     * @deprecated Use #getDimensionNames()
     */
    @Deprecated
    @Override
    public String[] getDimensionName() {
        return getDimensionNames();
    }

    /**
     * {@inheritDoc}
     *
     * @deprecated Use #setDimensionName(String[])
     */
    @Deprecated
    @Override
    public void setDimensionName(String[] names) {
        setDimensionNames(names);
    }

    @Override
    public void setDimensionName(int dim, String name) {
        dimNames[dim] = name;
    }

    @Override
    public String getDimensionName(int dim) {
        return dimNames[dim];
    }

    @Override
    public double getMaxOccurence() {
        // on creer un tableau dans cette classe, car on ne sait pas sur quelle
        // implantation on s'appuie. Mais dans les sous classes, si on a deja
        // un tableau il ne faut pas le recréer, on peut le passer directement
        int nbelem = 1;
        for (int i = 0; i < getDimCount(); i++) {
            nbelem *= getDim(i);
        }
        double[] data = new double[nbelem];
        int i = 0;
        for (MatrixIterator mi = iterator(); mi.next();) {
            data[i++] = mi.getValue();
        }
        return MatrixHelper.maxOccurence(data);
    }

    /**
     * {@inheritDoc}
     *
     * @deprecated use #getDimCount() instead
     */
    @Override
    public int getNbDim() {
        return getDimCount();
    }

    @Override
    public int getDimCount() {
        return dim.length;
    }

    @Override
    public int[] getDim() {
        return dim;
    }

    @Override
    public int getDim(int d) {
        return dim[d];
    }

    /**
     * Retourne la matrice elle meme. Les modifications sont faites directement
     * dessus
     */
    @Override
    public MatrixND map(MapFunction f) {
        for (MatrixIterator i = iterator(); i.next();) {
            i.setValue(f.apply(i.getValue()));
        }
        return this;
    }

    @Override
    public double getValue(Object[] coordinates) {
        return getValue(MatrixHelper.semanticsToDimension(getSemantics(),
                coordinates));
    }

    @Override
    public double getValue(Object x) {
        // on peut utiliser dimHelper car le get ne le reutilisera pas en
        // interne
        return getValue(dimHelper.get(x));
    }

    @Override
    public double getValue(Object x, Object y) {
        return getValue(dimHelper.get(x, y));
    }

    @Override
    public double getValue(Object x, Object y, Object z) {
        return getValue(dimHelper.get(x, y, z));
    }

    @Override
    public double getValue(Object x, Object y, Object z, Object t) {
        return getValue(dimHelper.get(x, y, z, t));
    }

    @Override
    public double getValue(int x) {
        // on peut utiliser dimHelper car le get ne le reutilisera pas en
        // interne
        return getValue(dimHelper.get(x));
    }

    @Override
    public double getValue(int x, int y) {
        return getValue(dimHelper.get(x, y));
    }

    @Override
    public double getValue(int x, int y, int z) {
        return getValue(dimHelper.get(x, y, z));
    }

    @Override
    public double getValue(int x, int y, int z, int t) {
        return getValue(dimHelper.get(x, y, z, t));
    }

    @Override
    public void setValue(Object[] coordinates, double d) {
        setValue(
                MatrixHelper.semanticsToDimension(getSemantics(), coordinates),
                d);
    }

    @Override
    public void setValue(Object x, double d) {
        setValue(dimHelper.get(x), d);
    }

    @Override
    public void setValue(Object x, Object y, double d) {
        setValue(dimHelper.get(x, y), d);
    }

    @Override
    public void setValue(Object x, Object y, Object z, double d) {
        setValue(dimHelper.get(x, y, z), d);
    }

    @Override
    public void setValue(Object x, Object y, Object z, Object t, double d) {
        setValue(dimHelper.get(x, y, z, t), d);
    }

    @Override
    public void setValue(int x, double d) {
        setValue(dimHelper.get(x), d);
    }

    @Override
    public void setValue(int x, int y, double d) {
        setValue(dimHelper.get(x, y), d);
    }

    @Override
    public void setValue(int x, int y, int z, double d) {
        setValue(dimHelper.get(x, y, z), d);
    }

    @Override
    public void setValue(int x, int y, int z, int t, double d) {
        setValue(dimHelper.get(x, y, z, t), d);
    }

    // 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 MatrixND && equals((MatrixND) o);
    }

    public boolean equals(MatrixND 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
     */
    @Override
    public boolean equalsValues(MatrixND mat) {
        boolean result = true;
        // les dimensions doivent-être identique
        result = result && MatrixHelper.sameDimension(getDim(), mat.getDim());

        // toutes les données doivent être identique
        for (MatrixIterator i = mat.iterator(); result && i.next();) {
            double v1 = i.getValue();
            double v2 = getValue(i.getCoordinates());
            result = v1 == v2;
            if (log.isTraceEnabled()) {
                log.trace("v1(" + v1 + ")==v2(" + v2 + ")=" + result);
            }
        }

        return result;
    }

    @Override
    public String toString() {
        StringBuffer result = new StringBuffer();
        result.append("dimensions = [\n");
        for (int i = 0; i < getDim().length; i++) {
            result.append(getDim()[i] + ",");
        }
        result.append("\n]\nmatrice = [\n");
        for (MatrixIterator i = this.iterator(); i.next();) {
            result.append(i.getValue() + ",");
        }
        result.append("\n]\n");
        return result.toString();
    }

    @Override
    public List toList() {
        List result = new ArrayList();
        // [3,2,5,4]
        for (MatrixIterator i = iterator(); i.next();) {
            int[] coord = i.getCoordinates();
            double value = i.getValue();
            List tmp = (List) result;
            for (int dim = 0; dim < coord.length - 1; dim++) {
                while (tmp.size() <= coord[dim]) {
                    tmp.add(new ArrayList());
                }
                tmp = (List) tmp.get(coord[dim]);
            }
            while (tmp.size() <= coord[coord.length - 1]) {
                tmp.add(NumberUtils.DOUBLE_ZERO);
            }

            tmp.set(coord[coord.length - 1], value);
        }

        return result;
    }

    @Override
    public void fromList(List list) {
        // on suppose que les listes sont bien formé, c-a-d qu'elles sont
        // toutes de la meme dimension pour une dimension donnée.
        ArrayIntList dim = new ArrayIntList();
        List tmp = list;
        while (tmp.get(tmp.size() - 1) instanceof List) {
            dim.add(tmp.size());
            tmp = (List) tmp.get(tmp.size() - 1);
        }
        dim.add(tmp.size());
        MatrixND mat = getFactory().create(dim.toArray());

        for (MatrixIterator i = mat.iterator(); i.next();) {
            int[] coord = i.getCoordinates();
            tmp = list;
            for (int d = 0; d < coord.length - 1; d++) {
                tmp = (List) tmp.get(coord[d]);
            }

            Double value = (Double) tmp.get(coord[coord.length - 1]);
            i.setValue(value);
        }
        paste(mat);
    }

    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;
    }

    @Override
    public double sumAll() {
        double result = 0;
        for (MatrixIterator i = iterator(); i.next();) {
            result += i.getValue();
        }
        return result;
    }

    @Override
    public MatrixND sumOverDim(int dim) {
        return sumOverDim(dim, getDim(dim));
    }

    @Override
    public MatrixND sumOverDim(int dim, int step) {
        if (step < 0) {
            step = getDim(dim);
        } else if (step <= 1) {
            // il n'y a rien a faire, on fait une copie et on la retrourne
            return getFactory().create(this);
        }

        // le nombre d'element qu'il y aura dans la dim pour le resultat
        int nbDim = getDim(dim) / step;

        List[] semantics = new List[getDimCount()];
        System.arraycopy(getSemantics(), 0, semantics, 0, getDimCount());
        semantics[dim] = semantics[dim].subList(0, nbDim);

        // creation du resultat
        MatrixND result = getFactory().create(getName(), semantics,
                getDimensionNames());

        for (int i = 0; i < result.getDim(dim); i++) {
            MatrixND temp = getSubMatrix(dim, i * step, step);
            MatrixND sum = result.getSubMatrix(dim, i, 1);
            for (int s = 0; s < temp.getDim(dim); s++) {
                sum.add(temp.getSubMatrix(dim, s, 1));
            }
        }
        return result;
    }

    @Override
    public MatrixND sumOverDim(int dim, int start, int nb) {
        // copie de l'ancienne semantique
        List[] semantics = new List[getDimCount()];

        System.arraycopy(getSemantics(), 0, semantics, 0, getDimCount());
        semantics[dim] = new ArrayList(semantics[dim]);

        // creation d'un liste qui agrege les elements sommés
        List newElem = new ArrayList();
        for (int i = 0; i < nb; i++) {
            newElem.add(semantics[dim].remove(start));
        }
        // on ajout la liste comme nouvel element de la semantique
        semantics[dim].add(start, newElem);

        // creation du resultat
        MatrixND result = getFactory().create(getName(), semantics,
                getDimensionNames());

        MatrixND sub1 = this.getSubMatrix(dim, 0, start);
        MatrixND sub2 = this.getSubMatrix(dim, start, nb).sumOverDim(dim);
        MatrixND sub3 = this.getSubMatrix(dim, start + nb, getDim(dim)
                - (start + nb));

        int[] origin = new int[getDimCount()];
        result.paste(origin, sub1);
        origin[dim] = start;
        result.paste(origin, sub2);
        if (start + 1 < result.getDim(dim)) {
            origin[dim] = start + 1;
            result.paste(origin, sub3);
        }

        return result;
    }

    @Override
    public MatrixND cut(int dim, int[] toCut) {
        throw new UnsupportedOperationException("Méthode non implantée");
    }

    /**
     * Modifie la matrice actuel en metant les valeurs de mat passé en parametre
     */
    @Override
    public MatrixND paste(MatrixND mat) {
        return paste(new int[getDimCount()], mat);
    }

    /**
     * Modifie la matrice actuel en metant les valeurs de mat passé en parametre
     *
     * @param origin le point d'origine a partir duquel on colle la matrice
     * @param mat une matrice avec le meme nombre de dimension, si la matrice
     *            que l'on colle est trop grande, les valeurs qui depasse ne
     *            sont pas prises en compte
     */
    @Override
    public MatrixND paste(int[] origin, MatrixND mat) {
        // TODO, si les matrice mat et this on les memes dimensions
        // et que origin est 0
        // on doit pouvoir optimiser en appeler une methode paste
        // sur BasicMatrix qui l'appel sur le vector
        if (mat != null) {
            for (MatrixIterator mi = mat.iterator(); mi.next();) {
                int[] coordinates = ArrayUtil.sum(origin, mi.getCoordinates());
                if (isValidCoordinates(coordinates)) {
                    setValue(coordinates, mi.getValue());
                }
            }
        }
        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é
     */
    @Override
    public MatrixND pasteSemantics(MatrixND mat) {
        if (mat != null) {
            for (MatrixIterator mi = mat.iterator(); mi.next();) {
                Object[] sems = mi.getSemanticsCoordinates();
                if (isValidCoordinates(sems)) {
                    setValue(sems, mi.getValue());
                }
            }
        }
        return this;
    }

    @Override
    public MatrixND 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);
    }

    @Override
    public MatrixND 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 MatrixND getSubMatrixOnSemantic(int dim, Object... elem) {
        MatrixND result = getSubMatrix(dim, elem);
        return result;
    }

    @Override
    public MatrixND 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);
    }

    @Override
    public MatrixND getSubMatrix(int dim, int[] elem) {
        return new SubMatrix(this, dim, elem);
    }

    /**
     * Modifie la matrice actuelle en lui ajoutant les valeurs de la matrice
     * passé en parametre. La matrice passé en parametre doit avoir le meme
     * nombre de dimension, et chacune de ses dimensions doit avoir un nombre
     * d'element au moins egal a cette matrice.
     */
    @Override
    public MatrixND add(MatrixND m) {
        // TODO si les dimensions sont exactment les memes, on doit pouvoir
        // gagner du temps en travaillant directement au niveau du vector
        for (MatrixIterator i = iterator(); i.next();) {
            i.setValue(i.getValue() + m.getValue(i.getCoordinates()));
            // TODO faire une variante de add avec les semantiques
        }
        return this;
    }

    /**
     * Modifie la matrice actuelle en lui soustrayant les valeurs de la matrice
     * passé en parametre. La matrice passé en parametre doit avoir le meme
     * nombre de dimension, et chacune de ses dimensions doit avoir un nombre
     * d'element au moins egal a cette matrice.
     */
    @Override
    public MatrixND minus(MatrixND m) {
        // TODO si les dimensions sont exactment les memes, on doit pouvoir
        // gagner du temps en travaillant directement au niveau du vector
        for (MatrixIterator i = iterator(); i.next();) {
            i.setValue(i.getValue() - m.getValue(i.getSemanticsCoordinates()));
        }
        return this;
    }

    @Override
    public MatrixND transpose() {
        MatrixND result = null;

        if (getDimCount() > 2) {
            throw new MatrixException(
                    "La transpose ne peut-être fait que sur une matrice ayant 2 dimensions ou moins");
        }
        if (getDimCount() == 1) {
            result = getFactory()
                    .create(
                            getName(),
                            new List[] { Collections.nCopies(1, null),
                                    getSemantic(0) },
                            new String[] { "Dimension 0", getDimensionName(0) });
            for (int x = 0; x < getDim(0); x++) {
                result.setValue(0, x, getValue(x));
            }
        } else {
            result = getFactory().create(getName(),
                    new List[] { getSemantic(1), getSemantic(0) },
                    new String[] { getDimensionName(1), getDimensionName(0) });
            for (int x = 0; x < getDim(0); x++) {
                for (int y = 0; y < getDim(1); y++) {
                    result.setValue(y, x, getValue(x, y));
                }
            }
        }
        return result;
    }

    @Override
    public MatrixND reduce() {
        return reduce(1);
    }

    @Override
    public MatrixND 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--;
            }
        }
        MatrixND result = reduce(dimName, sem, correspondance);
        return result;
    }

    @Override
    public MatrixND 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--;
            }
        }

        MatrixND 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 MatrixND 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] = (List) sem.get(nbDim - 1 - i);
            newDimNames[i] = (String) dimName.get(nbDim - 1 - i);
            tmpcorrespondance[i] = correspondance[nbDim - 1 - i];
        }
        correspondance = tmpcorrespondance;

        MatrixND result = getFactory().create(getName(), newSemantics,
                newDimNames);

        // on reprend les valeurs
        int[] newCoordinates = new int[result.getDimCount()];
        for (MatrixIterator mi = iterator(); mi.next();) {
            int[] oldCoordinates = mi.getCoordinates();
            for (int i = 0; i < newCoordinates.length; i++) {
                newCoordinates[i] = oldCoordinates[correspondance[i]];
            }
            result.setValue(newCoordinates, mi.getValue());
        }
        return result;
    }

    @Override
    public MatrixND mult(MatrixND m) throws MatrixException {
        if (this.getDimCount() > 2 || m.getDimCount() > 2) {
            throw new MatrixException(
                    "La multiplication de matrice n'est pas applicable aux matrices de plus de 2 dimensions");
        }
        if (!((this.getDim(1) == m.getDim(0)))) {
            throw new MatrixException(
                    "Le nombre de colonnes de la matrice m1 doit etre egal au nombre de lignes de la matrice m2");
        }

        MatrixND result = getFactory().create(
                new int[] { this.getDim(0), m.getDim(1) });
        double d;
        for (int x = 0; x < this.getDim(0); x++) {
            for (int y = 0; y < m.getDim(1); y++) {
                d = this.getValue(x, 0) * m.getValue(0, y);
                for (int k = 1; k < this.getDim(1); k++) {
                    d += this.getValue(x, k) * m.getValue(k, y);
                }
                result.setValue(x, y, d);
            }
        }
        return result;
    }

    @Override
    public MatrixND mults(final double d) {
        map(new MapFunction() {
            @Override
            public double apply(double val) {
                return val * d;
            }
        });
        return this;
    }

    @Override
    public MatrixND divs(final double d) {
        map(new MapFunction() {
            @Override
            public double apply(double val) {
                return val / d;
            }
        });
        return this;
    }

    @Override
    public MatrixND adds(final double d) {
        map(new MapFunction() {
            @Override
            public double apply(double val) {
                return val + d;
            }
        });
        return this;
    }

    @Override
    public MatrixND minuss(final double d) {
        map(new MapFunction() {
            @Override
            public double apply(double val) {
                return val - d;
            }
        });
        return this;
    }

    /**
     * Determine si la matrice supporte l'import et l'export CSV
     *
     * @return support du CSV
     */
    @Override
    public boolean isSupportedCSV() {
        return getDimCount() <= 2;
    }

    /**
     * Import depuis un reader au format CSV des données dans la matrice
     *
     * @param reader le reader à importer
     * @param origin le point à partir duquel il faut faire l'importation
     *            int[]{x,y}
     */
    @Override
    public void importCSV(Reader reader, int[] origin) throws IOException {
        int rowsCount = 0;
        List row = new ArrayList();
        StringBuffer number = new StringBuffer(20);
        boolean stop = false;

        for (int c = reader.read(); !stop; c = reader.read()) {
            if (c == -1) {
                stop = true;
            }
            if (c == ' ') {
                // skip space
            } else if (c == CSV_SEPARATOR) {
                if (NUMBER.matcher(number.toString()).matches()) {
                    Double val = Double.valueOf(number.toString());
                    row.add(val);
                }
                number.setLength(0);
            } else if (c == -1 || c == '\n' || c == '\r') {
                // is line return or equivalent char because space is already
                // skiped
                // or end of stream

                // at end of line, we must see if the leave number
                if (NUMBER.matcher(number.toString()).matches()) {
                    Double val = Double.valueOf(number.toString());
                    row.add(val);
                }
                number.setLength(0);

                if (!row.isEmpty()) {
                    if (getDim().length == 1) {
                        int columnNumber = origin[0];
                        for (Double value : row) {
                            if (columnNumber < getDim(0)) {
                                setValue(new int[] { columnNumber }, value);
                                columnNumber++;
                            }
                        }
                    } else if (getDim().length == 2) {
                        MatrixND matrix = getFactory().create(
                                new int[] { 1, row.size() });
                        int columnNumber = 0;
                        for (Double value : row) {
                            matrix.setValue(new int[] { 0, columnNumber },
                                    value);
                            columnNumber++;
                        }
                        paste(new int[] { origin[0] + rowsCount, origin[1] },
                                matrix);
                        rowsCount++;
                        row.clear();
                    } else {
                        throw new MatrixException(
                                "Can't import matrix with more than 2 dimensions.");
                    }
                }
            } else {
                number.append((char) c);
            }
        }

        // cette implatation avec StreamTokenizer ne fonctionne pas
        // car il ne sait pas reconnaitre tous les nombres: 5.0E-7
        // int rowsCount = 0;
        // StreamTokenizer tokenizer;
        // List row = new ArrayList();
        // boolean stop = false;
        //
        // tokenizer = new StreamTokenizer(reader);
        // tokenizer.eolIsSignificant(true);
        //
        // while(!stop) {
        // tokenizer.nextToken();
        //
        // switch (tokenizer.ttype) {
        // case StreamTokenizer.TT_EOF:
        // stop = true; // no break we do next case too
        // case StreamTokenizer.TT_EOL:
        // if(!row.isEmpty()) {
        // MatrixND matrix = getFactory().create(new int[]{1, row.size()});
        // int columnNumber = 0;
        // for (Double value : row) {
        // matrix.setValue(new int[]{0, columnNumber}, value);
        // columnNumber++;
        // }
        // paste(new int[]{origin[0] + rowsCount, origin[1]}, matrix);
        // rowsCount ++;
        // row.clear();
        // }
        // break;
        // case StreamTokenizer.TT_NUMBER:
        // System.out.println("+++++++++ " + tokenizer.nval);
        // row.add(tokenizer.nval);
        // break;
        // case StreamTokenizer.TT_WORD:
        // System.out.println("--------- " + tokenizer.nval);
        // break;
        // default:
        // break;
        // }
        // }
    }

    /**
     * Export dans un writer au format CSV de la matrice
     *
     * @param writer le writer ou copier la matrice
     * @param withSemantics export ou pas des semantiques de la matrice dans le
     *            writer
     */
    @Override
    public void exportCSV(Writer writer, boolean withSemantics)
            throws IOException {
        int dimsCount = getDimCount();
        int rowsCount = dimsCount == 1 ? 1 : getDim(0);
        int columnsCount = dimsCount == 1 ? getDim(0) : getDim(1);
        int[] coordinates;

        if (!isSupportedCSV()) {
            throw new UnsupportedOperationException();
        }

        /* Création de l'entete */
        if (withSemantics) {
            /* Recuperation de la liste sur la bonne dimenssion */
            List listSemantics = getSemantic(dimsCount - 1);
            /* Ajout d'un décalage de l'entete pour la dimenssion 2 */
            writer.append(dimsCount == 2 ? " " + CSV_SEPARATOR : "");
            for (Object semantic : listSemantics) {
                writer.append("\"" + semantic + "\"" + CSV_SEPARATOR);
            }
            writer.append("\n");
        }

        for (int rowNb = 0; rowNb < rowsCount; rowNb++) {
            /* Ajout de la semantic devant la ligne pour la dimenssion 2 */
            if (withSemantics && dimsCount == 2) {
                Object semantic = getSemantic(0).get(rowNb);
                writer.append("\"" + semantic + "\"" + CSV_SEPARATOR);
            }

            for (int columnNb = 0; columnNb < columnsCount; columnNb++) {
                /* Calcul des coordonnees */
                coordinates = dimsCount == 1 ? new int[] { columnNb }
                        : new int[] { rowNb, columnNb };
                writer.append(getValue(coordinates) + "" + CSV_SEPARATOR);
            }
            writer.append("\n");
        }
    }

} // AbstractMatrixND