View Javadoc
1   /*
2    * #%L
3    * Nuiton Utils
4    * %%
5    * Copyright (C) 2004 - 2012 CodeLutin, Chatellier Eric
6    * %%
7    * This program is free software: you can redistribute it and/or modify
8    * it under the terms of the GNU Lesser General Public License as 
9    * published by the Free Software Foundation, either version 3 of the 
10   * License, or (at your option) any later version.
11   * 
12   * This program is distributed in the hope that it will be useful,
13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   * GNU General Lesser Public License for more details.
16   * 
17   * You should have received a copy of the GNU General Lesser Public 
18   * License along with this program.  If not, see
19   * <http://www.gnu.org/licenses/lgpl-3.0.html>.
20   * #L%
21   */
22  package org.nuiton.util;
23  
24  import org.apache.commons.lang3.ObjectUtils;
25  import org.apache.commons.lang3.StringUtils;
26  import org.apache.commons.logging.Log;
27  import org.apache.commons.logging.LogFactory;
28  
29  import java.io.Serializable;
30  import java.util.*;
31  
32  /**
33   * Permet de stocker des informations dans une matrix a N dimension
34   * Si lors de l'ajout on indique une dimension qui n'existe pas encore ou
35   * un element dans une dimension qui n'existe pas, la matrice ajoute
36   * automatiquement les elements manquant pour que l'ajout se passe bien.
37   * <p>
38   * MatrixMap permet de stocker les elements avec des cles de n'importe quel
39   * type. Les coordonnees utilisant ces objets sont converti en coordonnees
40   * numeriques qui est la seul chose que sait gere Matrix. Ces coordonnees
41   * numeriques sont alors convertis en coordonnees lineaire pour le stockage
42   * dans Vector. On decoupe ainsi les problemes et on minimise le stockage et
43   * certain traitement sur les données puisqu'au final toutes les données sont
44   * dans une simple liste.
45   * <p>
46   * Pour créer une nouvelle matrice, il faut utiliser une des méthodes de
47   * {@link MatrixMap.Factory}
48   *
49   * @author Benjamin Poussin - poussin@codelutin.com
50   * @since 2.2.1
51   */
52  public interface MatrixMap<E> extends Iterable<E> {
53  
54      /**
55       * Classe permettant la creation de matrice
56       */
57      class Factory {
58          public static <T> MatrixMap<T> create(List... semantics) {
59              MatrixMap<T> result = new MatrixMapFixed<T>(semantics);
60              return result;
61          }
62  
63          public static <T> MatrixMap<T> create(String name, List... semantics) {
64              MatrixMap<T> result = new MatrixMapFixed<T>(name, semantics);
65              return result;
66          }
67  
68          public static <T> MatrixMap<T> create(String name, String[] dimNames, List... semantics) {
69              MatrixMap<T> result = new MatrixMapFixed<T>(name, dimNames, semantics);
70              return result;
71          }
72  
73          public static <T> MatrixMap<T> create(MatrixMap<T> matrix) {
74              MatrixMap<T> result = new MatrixMapFixed<T>(matrix);
75              return result;
76          }
77  
78          public static <T> MatrixMap<T> createElastic(List... semantics) {
79              MatrixMap<T> result = create(semantics);
80              result = createElastic(result);
81              return result;
82          }
83  
84          public static <T> MatrixMap<T> createElastic(String name, List... semantics) {
85              MatrixMap<T> result = create(name, semantics);
86              result = createElastic(result);
87              return result;
88          }
89  
90          public static <T> MatrixMap<T> createElastic(String name, String[] dimNames, List... semantics) {
91              MatrixMap<T> result = create(name, dimNames, semantics);
92              result = createElastic(result);
93              return result;
94          }
95  
96          public static <T> MatrixMap<T> createElastic(MatrixMap<T> matrix) {
97              MatrixMap<T> result = new MatrixMapElastic<T>(matrix);
98              return result;
99          }
100     }
101 
102     @Override
103     MatrixMapIterator<E> iterator();
104 
105     /**
106      * Copy la matrice pour pouvoir la modifier sans perdre les donnees
107      * initiales.
108      *
109      * @return new matrix
110      */
111     MatrixMap<E> copy();
112 
113     SemanticList[] getSemantics();
114 
115     SemanticList getSemantic(int dim);
116 
117     void setSemantic(int dim, List sem);
118 
119     void setName(String name);
120 
121     String getName();
122 
123     String[] getDimensionNames();
124 
125     void setDimensionNames(String[] names);
126 
127     void setDimensionName(int dim, String name);
128 
129     String getDimensionName(int dim);
130 
131     int getDimCount();
132 
133     int[] getDim();
134 
135     int getDim(int d);
136 
137     /**
138      * Applique sur chaque element de la matrice la fonction f
139      *
140      * @param f la fonction a appliquer
141      * @return Retourne la matrice elle meme. Les modifications sont faites directement
142      * dessus
143      */
144     MatrixMap<E> map(MapFunction<E> f);
145 
146     /**
147      * Retourne l'element a une certaine position en utilisant des indices
148      * ex: 2,3,1
149      *
150      * @param coordinates FIXME
151      * @return FIXME
152      */
153     E getValueIndex(int... coordinates);
154 
155     /**
156      * Modifie l'element a une certaine position en utilisant des indices
157      * ex: 2,3,1
158      *
159      * @param value       la nouvelle valeur
160      * @param coordinates FIXME
161      */
162     void setValueIndex(E value, int... coordinates);
163 
164     /**
165      * Retourne l'element a une certaine position en utilisant les semantiques
166      *
167      * @param coordinates FIXME
168      * @return FIXME
169      */
170     E getValue(Object... coordinates);
171 
172     /**
173      * Modifie l'element a une certaine position en utilisant les semantiques
174      *
175      * @param value       la nouvelle valeur
176      * @param coordinates FIXME
177      */
178     void setValue(E value, Object... coordinates);
179 
180     /**
181      * Verifie que deux matrices sont completement equals
182      * (dimension, semantique, nom, valeur, ...)
183      *
184      * @param mat FIXME
185      * @return FIXME
186      */
187     boolean equals(MatrixMap mat);
188 
189     /**
190      * Verifie si les matrices sont egales en ne regardant que les valeurs et
191      * pas les semantiques
192      *
193      * @param mat FIXME
194      * @return equality on values
195      */
196     boolean equalsValues(MatrixMap<E> mat);
197 
198     /**
199      * Representation string de la matrice quelque soit le nombre de dimension
200      *
201      * @return FIXME
202      */
203     String toStringGeneric();
204 
205     /**
206      * Indique si les semantiques passées en argument sont valable pour la
207      * matrice courante
208      *
209      * @param semantics FIXME
210      * @return FIXME
211      */
212     boolean isValidCoordinates(Object[] semantics);
213 
214     /**
215      * Copie une matrice dans la matrice actuelle. La matrice à copier à le même
216      * nombre de dimension. Si la matrice à copier est trop grande seul les
217      * éléments pouvant être copier le seront.
218      *
219      * @param mat la matrice à copier
220      * @return return la matrice courante.
221      */
222     MatrixMap paste(MatrixMap<E> mat);
223 
224     /**
225      * Permet de prendre une sous matrice dans la matrice courante. La sous
226      * matrice a le même nombre de dimensions mais sur une des dimensions on ne
227      * prend que certain élément.
228      *
229      * @param dim   la dimension dans lequel on veut une sous matrice
230      * @param start la position dans dim d'ou il faut partir pour prendre la
231      *              sous matrice. 0 ≤ start &lt; dim.size si start est négatif alors
232      *              la position de départ est calculé par rapport à la fin de la
233      *              dimension, pour avoir le dernier élément il faut passer -1
234      * @param nb    le nombre d'élément à prendre dans la dimension si nb est
235      *              inférieur ou égal à 0 alors cela indique qu'il faut prendre
236      *              tous les éléments jusqu'à la fin de la dimension.
237      * @return new matrix
238      */
239     MatrixMap<E> getSubMatrix(int dim, Object start, int nb);
240 
241     /**
242      * Permet de prendre une sous matrice dans la matrice courante. La sous
243      * matrice a le même nombre de dimensions mais sur une des dimensions on ne
244      * prend que certain élément.
245      *
246      * @param dim  la dimension dans lequel on veut une sous matrice
247      * @param elem les éléments dans la dimension à conserver
248      * @return new matrix
249      */
250     MatrixMap<E> getSubMatrix(int dim, Object... elem);
251 
252     /**
253      * Permet de prendre une sous matrice dans la matrice courante.
254      * <p>
255      * Réalise plusieurs appels à {@link #getSubMatrix(int, Object...)} suivant
256      * l'implémentation.
257      *
258      * @param elems les éléments dans la dimension à conserver
259      * @return new matrix
260      */
261     MatrixMap<E> getSubMatrix(Object[]... elems);
262 
263     /**
264      * Reduit la matrice de sorte que toutes les dimensions qui n'ont qu'un
265      * élement soit supprimée. Au pire cette méthode retourne une matrice à une
266      * seule dimension à un seul élément.
267      *
268      * @return une nouvelle matrice plus petite que la matrice actuelle ou egal
269      * s'il n'y a aucune dimension à supprimer
270      */
271     MatrixMap<E> reduce();
272 
273     /**
274      * Reduit le matrice seulement sur les dimensions passées en argument. Si
275      * une des dimensions passées en arguement n'a pas qu'un seul élément, cette
276      * dimension n'est pas prise en compte.
277      *
278      * @param dims les dimensions sur lequel il faut faire la reduction
279      * @return une nouvelle matrice
280      */
281     MatrixMap<E> reduceDims(int... dims);
282 
283     /**
284      * Reduit la matrice de sorte que toutes les dimensions qui n'ont qu'un
285      * élement soit supprimée. Au pire cette méthode retourne une matrice à une
286      * seule dimension à un seul élément.
287      *
288      * @param minNbDim le nombre minimum de dimension que l'on souhaite pour la
289      *                 matrice résultat
290      * @return une nouvelle matrice plus petite que la matrice actuelle ou egal
291      * s'il n'y a aucune dimension à supprimer
292      */
293     MatrixMap<E> reduce(int minNbDim);
294 
295     /**
296      * Permet de retourner une nouvelle matrice ayant les semantiques passées
297      * en parametre. La nouvelle matrice contient les données de l'ancienne
298      * matrice par copie en fonction des semantiques
299      *
300      * @param sems FIXME
301      * @return FIXME
302      */
303     MatrixMap<E> extend(Object... sems);
304 
305     ///////////////////////////////////////////////////////////////////////////
306     //
307     // C L A S S E    I N T E R N E
308     //
309     ///////////////////////////////////////////////////////////////////////////
310 
311     /**
312      * Classe contenant des méthodes statiques pour aider a la manipulation
313      * des matrices
314      */
315     class MatrixHelper {
316 
317         /**
318          * Mais en forme un texte pour qu'il fasse exactement la longueur
319          * demandee (length). Si length est possitif alors s'il y besoin
320          * d'ajouter des espaces, ils seront mis devant le texte, sinon il
321          * seront mis apres le texte
322          *
323          * @param o           l'objet a convertir en string
324          * @param length      la longueur de representation souhaite
325          * @param valueIfNull la valeur a utilise si l'objet est null
326          * @return FIXME
327          */
328         public static String format(Object o, int length, String valueIfNull) {
329             if (o == null) {
330                 o = valueIfNull;
331             }
332             int absLength = Math.abs(length);
333 
334             String result = String.valueOf(o);
335             if (absLength > 3) {
336                 result = StringUtils.abbreviate(result, absLength);
337             }
338             if (length < 0) {
339                 result = StringUtils.leftPad(result, absLength);
340             } else if (length > 0) {
341                 result = StringUtils.rightPad(result, absLength);
342             }
343 
344             return result;
345         }
346 
347         /**
348          * Permet de convertir des coordonnées définies par des entiers en coordonnées
349          * semantique par des objets
350          *
351          * @param semantics   la semantique à utilisé pour la conversion
352          * @param coordinates les coordonnées à convertir
353          * @return un tableau donnant les coordonnées sous forme semantique s'il n'y
354          * a pas de semantique (liste pleine de null) alors un objet Integer
355          * est créer pour représenter la semantique de la dimension.
356          */
357         public static Object[] dimensionToSemantics(List[] semantics,
358                                                     int[] coordinates) {
359             Object[] result = new Object[coordinates.length];
360             for (int i = 0; i < result.length; i++) {
361                 result[i] = semantics[i].get(coordinates[i]);
362             }
363             return result;
364         }
365 
366         /**
367          * Permet de convertir des coordonnées sémantiques en coordonnées défini par
368          * des entiers. Cette fonction est l'inverse de
369          * {@link #dimensionToSemantics}.
370          *
371          * @param semantics   la semantique à utiliser pour la conversion
372          * @param coordinates les coordonnées sémantique
373          * @return les coordonnées en entier.
374          */
375         public static int[] semanticsToDimension(List[] semantics,
376                                                  Object[] coordinates) {
377             int[] result = new int[coordinates.length];
378             for (int i = 0; i < coordinates.length; i++) {
379                 result[i] = indexOf(semantics, i, coordinates[i]);
380             }
381             return result;
382         }
383 
384         /**
385          * Permet de retrouver la position d'un objet dans une liste
386          *
387          * @param semantics la semantique à utilisé pour la recherche
388          * @param dim       la dimension dans lequel il faut faire la recherche
389          * @param o         l'objet à rechercher
390          * @return la position de l'objet dans la dimension demandée
391          * @throws NoSuchElementException If element doesn't exists
392          */
393         public static int indexOf(List[] semantics, int dim, Object o)
394                 throws NoSuchElementException {
395             int result = -1;
396             if ((0 <= dim) && (dim < semantics.length)) {
397                 result = semantics[dim].indexOf(o);
398             }
399             if (result == -1) {
400                 throw new NoSuchElementException(
401                         "L'objet passé en argument n'a pas été retrouvé ou la dimension donnée ne convient pas:"
402                                 + o + " in " + semantics[dim]);
403             }
404             return result;
405         }
406 
407         /**
408          * Permet de savoir si deux dimension sont identiques.
409          *
410          * @param dim1 first dimensions
411          * @param dim2 second dimensions
412          * @return dimension equality
413          */
414         public static boolean sameDimension(int[] dim1, int[] dim2) {
415             return Arrays.equals(dim1, dim2);
416         }
417 
418     }
419 
420     /**
421      * Iterateur de matrice
422      *
423      * @param <E> FIXME
424      */
425     interface MatrixMapIterator<E> extends Iterator<E> {
426         int[] getCoordinates();
427 
428         E getValue();
429 
430         void setValue(E value);
431 
432         Object[] getSemanticsCoordinates();
433     }
434 
435     class MatrixMapIteratorImpl<E> implements MatrixMapIterator<E> { // MatrixMapIteratorImpl
436 
437         protected MatrixIterator<E> iterator = null;
438 
439         protected List[] semantics = null;
440 
441         protected int pos = 0;
442 
443         /**
444          * @param iterator  la matrice sur lequel l'iterator doit travailler
445          * @param semantics la semantique de matrix, si matrix n'a pas de semantique
446          *                  alors il faut passer null
447          */
448         public MatrixMapIteratorImpl(MatrixIterator<E> iterator, List[] semantics) {
449             this.iterator = iterator;
450             this.semantics = semantics;
451             pos = 0;
452         }
453 
454         @Override
455         public boolean hasNext() {
456             return iterator.hasNext();
457         }
458 
459         @Override
460         public E next() {
461             return iterator.next();
462         }
463 
464         @Override
465         public void remove() {
466             iterator.remove();
467         }
468 
469         public int[] getCoordinates() {
470             return iterator.getCoordinates();
471         }
472 
473         public E getValue() {
474             return iterator.getValue();
475         }
476 
477         public void setValue(E value) {
478             iterator.setValue(value);
479         }
480 
481         public Object[] getSemanticsCoordinates() {
482             Object[] result = null;
483             if (semantics != null) {
484                 int[] coordinates = getCoordinates();
485                 result = MatrixHelper.dimensionToSemantics(semantics,
486                         coordinates);
487             }
488             return result;
489         }
490 
491     } // MatrixMapIteratorImpl
492 
493     /**
494      * Collection particuliere utilisee pour la stockage des semantiques.
495      * <p>
496      * Sert a optimiser la recherche de la position d'une donnee dans la liste.
497      * Permet aussi de verifier qu'on ajoute pas de doublon dans la liste
498      *
499      * @param <T> FIXME
500      */
501     class SemanticList<T> extends AbstractList<T> implements RandomAccess {
502 
503         protected ArrayList<T> datas = null;
504 
505         protected Map<T, Integer> index = new HashMap<T, Integer>();
506 
507         public SemanticList() {
508             this(new ArrayList<T>());
509         }
510 
511         public SemanticList(Collection<T> c) {
512             datas = new ArrayList<T>(c);
513         }
514 
515         /*
516          * @see java.util.AbstractList#get(int)
517          */
518         @Override
519         public T get(int index) {
520             T result = datas.get(index);
521             return result;
522         }
523 
524         @Override
525         public void add(int index, T element) {
526             datas.add(index, element);
527             this.index.clear();
528         }
529 
530         @Override
531         public T set(int index, T element) {
532             T result = datas.set(index, element);
533             this.index.clear();
534             return result;
535         }
536 
537         @Override
538         public T remove(int index) {
539             T result = super.remove(index);
540             this.index.clear();
541             return result;
542         }
543 
544 
545         /*
546          * @see java.util.AbstractCollection#size()
547          */
548         @Override
549         public int size() {
550             int result = datas.size();
551             return result;
552         }
553 
554         /*
555          * @see java.util.AbstractList#indexOf(java.lang.Object)
556          */
557         @Override
558         public int indexOf(Object o) {
559             Map<T, Integer> index = getIndex();
560             Integer result = index.get(o);
561             int resultIndex = -1;
562             if (result != null) {
563                 resultIndex = result.intValue();
564             }
565             return resultIndex;
566         }
567 
568         protected Map<T, Integer> getIndex() {
569             if (index.isEmpty()) {
570                 for (int i = 0; i < datas.size(); i++) {
571                     index.put(datas.get(i), Integer.valueOf(i));
572                 }
573             }
574             return index;
575         }
576     }
577 
578     /**
579      * Implantation particuliere de matrice, qui lorsqu'on lui passe des
580      * dimension qui n'existe pas, elle les ajoutes dans les semantiques. Ceci
581      * n'est vrai que pour le set avec des semantiques, le set avec des indices
582      * ne rend pas la matrice elastique.
583      * <p>
584      * Cette classe fonctionne avec une matrice interne que l'on change lorsque
585      * l'on a besoin de modifier les dimensions. Le changement de dimension
586      * a donc un cout (creation d'une nouvelle matrice, copie des elements)
587      * <p>
588      * Si on cree une sous matrice, et que l'on modifie la matrice mere
589      * La sous matrice n'est pas impacter, puisqu'elle est base sur l'ancienne
590      * represention interne de la matrice elastique, les deux matrices n'ont donc
591      * plus de lien.
592      * <p>
593      * Les methodes reduce et extend retourne de nouvelle matrice qui ne sont
594      * pas elastique. Si on veut qu'elle le soit, il faut les reencapsuler
595      *
596      * @param <E> FIXME
597      */
598     class MatrixMapElastic<E> implements MatrixMap<E> {
599 
600         protected MatrixMap<E> internalMatrixMap;
601 
602         public MatrixMapElastic() {
603             internalMatrixMap = Factory.create();
604         }
605 
606         public MatrixMapElastic(MatrixMap<E> m) {
607             setInternalMatrixMap(m);
608         }
609 
610         public MatrixMap<E> getInternalMatrixMap() {
611             return internalMatrixMap;
612         }
613 
614         public void setInternalMatrixMap(MatrixMap<E> internalMatrixMap) {
615             this.internalMatrixMap = internalMatrixMap;
616         }
617 
618         public MatrixMapIterator<E> iterator() {
619             return getInternalMatrixMap().iterator();
620         }
621 
622         public MatrixMap<E> copy() {
623             return getInternalMatrixMap().copy();
624         }
625 
626         public SemanticList[] getSemantics() {
627             return getInternalMatrixMap().getSemantics();
628         }
629 
630         public SemanticList getSemantic(int dim) {
631             return getInternalMatrixMap().getSemantic(dim);
632         }
633 
634         public void setSemantic(int dim, List sem) {
635             getInternalMatrixMap().setSemantic(dim, sem);
636         }
637 
638         public void setName(String name) {
639             getInternalMatrixMap().setName(name);
640         }
641 
642         public String getName() {
643             return getInternalMatrixMap().getName();
644         }
645 
646         public String[] getDimensionNames() {
647             return getInternalMatrixMap().getDimensionNames();
648         }
649 
650         public void setDimensionNames(String[] names) {
651             getInternalMatrixMap().setDimensionNames(names);
652         }
653 
654         public void setDimensionName(int dim, String name) {
655             getInternalMatrixMap().setDimensionName(dim, name);
656         }
657 
658         public String getDimensionName(int dim) {
659             return getInternalMatrixMap().getDimensionName(dim);
660         }
661 
662         public int getDimCount() {
663             return getInternalMatrixMap().getDimCount();
664         }
665 
666         public int[] getDim() {
667             return getInternalMatrixMap().getDim();
668         }
669 
670         public int getDim(int d) {
671             return getInternalMatrixMap().getDim(d);
672         }
673 
674         public MatrixMap<E> map(MapFunction<E> f) {
675             return getInternalMatrixMap().map(f);
676         }
677 
678         public E getValueIndex(int... coordinates) {
679             return getInternalMatrixMap().getValueIndex(coordinates);
680         }
681 
682         public void setValueIndex(E value, int... coordinates) {
683             // la matrice est elastique que pour le set avec des semantics
684             getInternalMatrixMap().setValueIndex(value, coordinates);
685         }
686 
687         public E getValue(Object... coordinates) {
688             return getInternalMatrixMap().getValue(coordinates);
689         }
690 
691         public void setValue(E value, Object... coordinates) {
692             // check si les coordonnees sont valide.
693             // si non valide alors on extend la matrice interne
694             // et on appelle sur la nouvelle matrice interne
695             if (!isValidCoordinates(coordinates)) {
696                 MatrixMap<E> newMatrixMap = getInternalMatrixMap().extend(coordinates);
697                 setInternalMatrixMap(newMatrixMap);
698             }
699             getInternalMatrixMap().setValue(value, coordinates);
700         }
701 
702         @Override
703         public boolean equals(Object obj) {
704             return getInternalMatrixMap().equals(obj);
705         }
706 
707         public boolean equals(MatrixMap mat) {
708             return getInternalMatrixMap().equals(mat);
709         }
710 
711         public boolean equalsValues(MatrixMap<E> mat) {
712             return getInternalMatrixMap().equalsValues(mat);
713         }
714 
715         @Override
716         public String toString() {
717             return getInternalMatrixMap().toString();
718         }
719 
720         public String toStringGeneric() {
721             return getInternalMatrixMap().toStringGeneric();
722         }
723 
724         public boolean isValidCoordinates(Object[] semantics) {
725             return getInternalMatrixMap().isValidCoordinates(semantics);
726         }
727 
728         public MatrixMap paste(MatrixMap<E> mat) {
729             return getInternalMatrixMap().paste(mat);
730         }
731 
732         public MatrixMap<E> getSubMatrix(int dim, Object start, int nb) {
733             return getInternalMatrixMap().getSubMatrix(dim, start, nb);
734         }
735 
736         public MatrixMap<E> getSubMatrix(int dim, Object... elem) {
737             return getInternalMatrixMap().getSubMatrix(dim, elem);
738         }
739 
740         public MatrixMap<E> getSubMatrix(Object[]... elems) {
741             return getInternalMatrixMap().getSubMatrix(elems);
742         }
743 
744         public MatrixMap<E> reduce() {
745             return getInternalMatrixMap().reduce();
746         }
747 
748         public MatrixMap<E> reduceDims(int... dims) {
749             return getInternalMatrixMap().reduceDims(dims);
750         }
751 
752         public MatrixMap<E> reduce(int minNbDim) {
753             return getInternalMatrixMap().reduce(minNbDim);
754         }
755 
756         public MatrixMap<E> extend(Object... sems) {
757             return getInternalMatrixMap().extend(sems);
758         }
759 
760     }
761 
762     /**
763      * Implantation de MatrixMap dont les dimensions sont fixees a la creation
764      * Les dimensions ne change plus par la suite
765      *
766      * @param <E> FIXME
767      */
768     class MatrixMapFixed<E> extends AbstractMatrixMap<E> {
769         /**
770          * Logger.
771          */
772         private static final Log log = LogFactory.getLog(MatrixMapFixed.class);
773 
774         protected Matrix<E> matrix = null;
775 
776         public MatrixMapFixed(List... semantics) {
777             super(semantics);
778         }
779 
780         public MatrixMapFixed(String name, List... semantics) {
781             this(semantics);
782             setName(name);
783         }
784 
785         public MatrixMapFixed(String name, String[] dimNames, List... semantics) {
786             this(name, semantics);
787             for (int i = 0; dimNames != null && i < dimNames.length; i++) {
788                 setDimensionName(i, dimNames[i]);
789             }
790         }
791 
792         public MatrixMapFixed(MatrixMap<E> matrix) {
793             this(matrix.getName(), matrix.getDimensionNames(), matrix.getSemantics());
794             this.pasteIndex(matrix);
795         }
796 
797         protected Matrix<E> getMatrix() {
798             if (matrix == null) {
799                 matrix = new Matrix<E>(getDim());
800             }
801             return matrix;
802         }
803 
804         @Override
805         public MatrixMapIterator<E> iterator() {
806             return new MatrixMapIteratorImpl<E>(getMatrix().iterator(), getSemantics());
807         }
808 
809         @Override
810         public MatrixMap<E> map(MapFunction<E> f) {
811             getMatrix().data.map(f);
812             return this;
813         }
814 
815         @Override
816         public E getValueIndex(int... coordinates) {
817             if (coordinates.length == 0) {
818                 throw new IllegalArgumentException("Coordinates must not be empty");
819             }
820             return getMatrix().getValue(coordinates);
821         }
822 
823         /**
824          * Modifie un element de la matrice en fonction des dimensions passé en
825          * paramètre.<br>
826          * <p>
827          * Exemple: Si on a un matrice 3D.<br>
828          * m.set(v, [1,1,1]) modifie un element de la matrice.<br>
829          *
830          * @param value       la value a inserer
831          * @param coordinates les coordonées où faire le remplacement
832          */
833         @Override
834         public void setValueIndex(E value, int... coordinates) {
835             if (coordinates.length == 0) {
836                 throw new IllegalArgumentException("Coordinates must not be empty");
837             }
838             getMatrix().setValue(coordinates, value);
839         }
840 
841         /**
842          * Copie une matrice dans la matrice actuelle. La matrice à copier à le même
843          * nombre de dimension. Si la matrice à copier est trop grande seul les
844          * éléments pouvant être copier le seront.
845          *
846          * @param origin le point à partir duquel il faut faire la copie
847          * @param mat    la matrice à copier
848          * @return return la matrice courante.
849          */
850         public MatrixMap<E> paste(int[] origin, MatrixMap<E> mat) {
851             if (mat != null) {
852                 // si les matrice mat et this on les memes dimensions
853                 // et que origin est 0, on optimise en appeler une methode paste
854                 // sur Matrix qui l'appel sur le vector
855 
856                 // permet de savoir si l'origin est bien le point 0 de la matrice
857                 boolean origin0 = true;
858                 for (int i = 0; i < origin.length && origin0; i++) {
859                     origin0 = origin0 && origin[i] == 0;
860                 }
861                 if (origin0
862                         && mat instanceof MatrixMapFixed
863                         && Arrays.equals(mat.getDim(), this.getDim())) {
864                     getMatrix().data.paste(((MatrixMapFixed<E>) mat).getMatrix().data);
865                 } else {
866                     super.paste(origin, mat);
867                 }
868             }
869             return this;
870         }
871 
872     }
873 
874     /**
875      * Classe abstraite permettant de facilement implanter les matrice fixe,
876      * elastique et submatrix
877      *
878      * @param <E> FIXME
879      */
880     abstract class AbstractMatrixMap<E> implements MatrixMap<E> {
881 
882         /**
883          * Logger.
884          */
885         private static final Log log = LogFactory.getLog(AbstractMatrixMap.class);
886 
887         protected String name = null;
888 
889         protected String[] dimNames = null;
890 
891         protected int[] dim = null;
892 
893         protected SemanticList[] semantics = null;
894 
895         protected void init(int[] dim) {
896             this.dim = new int[dim.length];
897             System.arraycopy(dim, 0, this.dim, 0, dim.length);
898             semantics = new SemanticList[dim.length];
899             dimNames = new String[dim.length];
900         }
901 
902         protected AbstractMatrixMap(int[] dim) {
903             init(dim);
904             for (int i = 0; i < getDimCount(); i++) {
905                 // par defaut les listes des semantiques contiennent des nulls
906                 // FIXME no multiple null allowed
907                 setSemantic(i, Collections.nCopies(dim[i], null));
908             }
909         }
910 
911         public AbstractMatrixMap(List... semantics) {
912             int[] dim = new int[semantics.length];
913             for (int i = 0; i < dim.length; i++) {
914                 if (semantics[i] == null) {
915                     dim[i] = 0;
916                 } else {
917                     dim[i] = semantics[i].size();
918                 }
919             }
920             init(dim);
921             for (int i = 0; i < getDimCount(); i++) {
922                 setSemantic(i, semantics[i]);
923             }
924         }
925 
926         protected AbstractMatrixMap(String name, int[] dim) {
927             this(dim);
928             setName(name);
929         }
930 
931         protected AbstractMatrixMap(String name, int[] dim, String[] dimNames) {
932             this(dim);
933             setName(name);
934             for (int i = 0; dimNames != null && i < dimNames.length; i++) {
935                 setDimensionName(i, dimNames[i]);
936             }
937         }
938 
939         public AbstractMatrixMap(String name, List... semantics) {
940             this(semantics);
941             setName(name);
942         }
943 
944         public AbstractMatrixMap(String name, String[] dimNames, List... semantics) {
945             this(name, semantics);
946             for (int i = 0; dimNames != null && i < dimNames.length; i++) {
947                 setDimensionName(i, dimNames[i]);
948             }
949         }
950 
951         public AbstractMatrixMap(MatrixMap<E> matrix) {
952             this(matrix.getName(), matrix.getDimensionNames(), matrix.getSemantics());
953             this.pasteIndex(matrix);
954         }
955 
956         /**
957          * Copy la matrice pour pouvoir la modifier sans perdre les donnees
958          * initiales.
959          *
960          * @return new matrix
961          */
962         public MatrixMap<E> copy() {
963             MatrixMap<E> result = new MatrixMapFixed<E>(this);
964             return result;
965         }
966 
967         @Override
968         public MatrixMap clone() {
969             return copy();
970         }
971 
972         public SemanticList[] getSemantics() {
973             return semantics;
974         }
975 
976         public SemanticList getSemantic(int dim) {
977             return semantics[dim];
978         }
979 
980         public void setSemantic(int dim, List sem) {
981             // make copy because this matrix can change semantics
982             SemanticList l = new SemanticList(sem);
983             semantics[dim] = l;
984         }
985 
986         public void setName(String name) {
987             this.name = name;
988         }
989 
990         public String getName() {
991             return name;
992         }
993 
994         public String[] getDimensionNames() {
995             return dimNames;
996         }
997 
998         public void setDimensionNames(String[] names) {
999             for (int i = 0; names != null && i < names.length; i++) {
1000                 setDimensionName(i, names[i]);
1001             }
1002         }
1003 
1004         public void setDimensionName(int dim, String name) {
1005             dimNames[dim] = name;
1006         }
1007 
1008         public String getDimensionName(int dim) {
1009             return dimNames[dim];
1010         }
1011 
1012         public int getDimCount() {
1013             return dim.length;
1014         }
1015 
1016         public int[] getDim() {
1017             return dim;
1018         }
1019 
1020         public int getDim(int d) {
1021             return dim[d];
1022         }
1023 
1024         /**
1025          * Retourne la matrice elle meme. Les modifications sont faites directement
1026          * dessus
1027          */
1028         @Override
1029         public MatrixMap<E> map(MapFunction<E> f) {
1030             for (MatrixMapIterator<E> i = iterator(); i.hasNext(); ) {
1031                 i.setValue(f.apply(i.next()));
1032             }
1033             return this;
1034         }
1035 
1036         public E getValue(Object... coordinates) {
1037             if (coordinates.length == 0) {
1038                 throw new IllegalArgumentException("Coordinates must not be empty");
1039             }
1040             int[] intCoordinates =
1041                     MatrixHelper.semanticsToDimension(getSemantics(), coordinates);
1042             E result = getValueIndex(intCoordinates);
1043             return result;
1044         }
1045 
1046         public void setValue(E value, Object... coordinates) {
1047             if (coordinates.length == 0) {
1048                 throw new IllegalArgumentException("Coordinates must not be empty");
1049             }
1050             int[] intCoordinates =
1051                     MatrixHelper.semanticsToDimension(getSemantics(), coordinates);
1052             setValueIndex(value, intCoordinates);
1053         }
1054 
1055         // TODO peut-etre faire une variante de equals qui regarde par rapport au
1056         // coordonnées sémantique
1057         @Override
1058         public boolean equals(Object o) {
1059             return o instanceof MatrixMap && equals((MatrixMap) o);
1060         }
1061 
1062         public boolean equals(MatrixMap mat) {
1063             boolean result = true;
1064             // le nom doit être le même
1065             result = result && getName().equals(mat.getName());
1066 
1067             result = result && equalsValues(mat);
1068 
1069             // les sémantiques doivent-être identique
1070             for (int i = 0; result && i < getDimCount(); i++) {
1071                 String dimName1 = getDimensionName(i);
1072                 String dimName2 = mat.getDimensionName(i);
1073                 result = Objects.equals(dimName1, dimName2);
1074                 if (log.isTraceEnabled()) {
1075                     log.trace("dimName1(" + dimName1 + ")==dimName2(" + dimName2
1076                             + ")=" + result);
1077                 }
1078                 // System.out.println("dimName1("+dimName1+")==dimName2("+dimName2+
1079                 // ")="+result);
1080 
1081                 List sem1 = getSemantic(i);
1082                 List sem2 = mat.getSemantic(i);
1083                 result = result && Objects.equals(sem1, sem2);
1084                 if (log.isTraceEnabled()) {
1085                     log.trace("sem1(" + sem1 + ")==sem2(" + sem2 + ")=" + result);
1086                 }
1087                 // System.out.println("sem1("+sem1+")==sem1("+sem2+ ")="+result);
1088             }
1089 
1090             if (log.isTraceEnabled()) {
1091                 log.trace("result=" + result);
1092             }
1093             // System.out.println("result="+result);
1094             return result;
1095         }
1096 
1097         /**
1098          * Verifie si les matrices sont egales en ne regardant que les valeurs et
1099          * pas les semantiques
1100          *
1101          * @param mat FIXME
1102          * @return equality on values
1103          */
1104         public boolean equalsValues(MatrixMap mat) {
1105             boolean result = true;
1106             // les dimensions doivent-être identique
1107             result = result && MatrixHelper.sameDimension(getDim(), mat.getDim());
1108 
1109             // toutes les données doivent être identique
1110             for (MatrixMapIterator<E> i = mat.iterator(); result && i.hasNext(); ) {
1111                 E v1 = i.next();
1112                 E v2 = getValueIndex(i.getCoordinates());
1113                 result = v1 == v2;
1114                 if (log.isTraceEnabled()) {
1115                     log.trace("v1(" + v1 + ")==v2(" + v2 + ")=" + result);
1116                 }
1117             }
1118 
1119             return result;
1120         }
1121 
1122         /**
1123          * Si la matrice est 1D
1124          * <pre>
1125          * MaMatrice(matrix1D) [
1126          * MaDimName: Dim1, Dim2, Dim3,
1127          *              v1,   v2,   v3
1128          * ]
1129          * </pre>
1130          * <p>
1131          * Si la matrice est 2D
1132          * <pre>
1133          * MaMatrice(matrix2D) [
1134          *            MaDimX
1135          * MaDimY     Dim1, Dim2, Dim3,
1136          * DimA       v1,     v2,   v3
1137          * DimB       v4,     v5,   v6
1138          * DimC       v7,     v8,   v9
1139          * ]
1140          * </pre>
1141          * <p>
1142          * Pour les autres types de matrice la methode {@link #toStringGeneric() }
1143          * est utilise
1144          *
1145          * @return FIXME
1146          */
1147         @Override
1148         public String toString() {
1149             int LENGTH = 10;
1150             StringBuilder result = new StringBuilder();
1151             if (getDimCount() == 1) {
1152                 result.append(MatrixHelper.format(getName(), -LENGTH, "#NoNameMat"));
1153                 result.append("(matrix1D)[\n");
1154                 String dimName = getDimensionName(0);
1155                 result.append(MatrixHelper.format(dimName, LENGTH, "#NoNameDim"));
1156                 for (Object sem : getSemantic(0)) {
1157                     result.append(",");
1158                     result.append(MatrixHelper.format(sem, -LENGTH, null));
1159                 }
1160                 result.append(StringUtils.repeat(" ", LENGTH + 1));
1161                 for (int i = 0; i < getDim(0); i++) {
1162                     Object v = getValueIndex(i);
1163                     result.append(MatrixHelper.format(v, -LENGTH, null) + ",");
1164                 }
1165                 result.append("\n]");
1166             } else if (getDimCount() == 2) {
1167                 int[] pos = new int[2];
1168                 result.append(MatrixHelper.format(getName(), -LENGTH, "#NoNameMat"));
1169                 result.append("(matrix2D) [\n");
1170 
1171                 result.append(StringUtils.repeat(" ", LENGTH + 1));
1172                 String dimNameX = getDimensionName(0);
1173                 result.append(MatrixHelper.format(dimNameX, LENGTH, "#DimX"));
1174                 result.append("\n");
1175                 String dimNameY = getDimensionName(1);
1176                 result.append(MatrixHelper.format(dimNameY, LENGTH, "#DimY"));
1177                 result.append(" ");
1178                 for (Object sem : getSemantic(0)) {
1179                     result.append(MatrixHelper.format(sem, -LENGTH, null));
1180                     result.append(",");
1181                 }
1182 
1183                 for (int y = 0; y < getDim(1); y++) {
1184                     result.append("\n");
1185                     Object sem = getSemantic(1).get(y);
1186                     result.append(MatrixHelper.format(sem, LENGTH, null));
1187                     result.append(" ");
1188                     for (int x = 0; x < getDim(0); x++) {
1189                         pos[0] = x;
1190                         pos[1] = y;
1191                         Object v = getValueIndex(pos);
1192                         result.append(MatrixHelper.format(v, -LENGTH, null) + ",");
1193                     }
1194                 }
1195                 result.append("\n]");
1196             } else {
1197                 result.append(toStringGeneric());
1198             }
1199             return result.toString();
1200         }
1201 
1202         /**
1203          * Representation string de la matrice quelque soit le nombre de dimension
1204          *
1205          * @return FIXME
1206          */
1207         public String toStringGeneric() {
1208             StringBuilder result = new StringBuilder();
1209             result.append(MatrixHelper.format(getName(), 0, "#NoNameMat"));
1210             result.append("(matrix" + getDimCount() + "D)[\n");
1211             result.append("dimensions = [");
1212             for (int i = 0; i < getDim().length; i++) {
1213                 result.append(getDim()[i] + ",");
1214             }
1215             result.append("]\ndata = [");
1216             for (MatrixMapIterator i = this.iterator(); i.hasNext(); ) {
1217                 result.append(i.next() + ",");
1218             }
1219             result.append("]\n");
1220             return result.toString();
1221         }
1222 
1223         public boolean isValidCoordinates(int[] dim) {
1224             boolean result = getDimCount() == dim.length;
1225             for (int i = 0; result && i < dim.length; i++) {
1226                 result = 0 <= dim[i] && dim[i] < getDim(i);
1227             }
1228             return result;
1229         }
1230 
1231         public boolean isValidCoordinates(Object[] semantics) {
1232             boolean result = getDimCount() == semantics.length;
1233             for (int i = 0; result && i < semantics.length; i++) {
1234                 List semantic = getSemantic(i);
1235                 result = semantic.contains(semantics[i]);
1236             }
1237             return result;
1238         }
1239 
1240         /**
1241          * Copie une matrice dans la matrice actuelle. La matrice à copier à le même
1242          * nombre de dimension. Si la matrice à copier est trop grande seul les
1243          * éléments pouvant être copier le seront.
1244          *
1245          * @param mat la matrice à copier
1246          * @return return la matrice courante.
1247          */
1248         public MatrixMap pasteIndex(MatrixMap<E> mat) {
1249             return paste(new int[getDimCount()], mat);
1250         }
1251 
1252         protected MatrixMap<E> paste(int[] origin, MatrixMap<E> mat) {
1253             if (mat != null) {
1254                 for (MatrixMapIterator<E> mi = mat.iterator(); mi.hasNext(); ) {
1255                     E value = mi.next();
1256                     int[] coordinates = ArrayUtil.sum(origin, mi.getCoordinates());
1257                     if (isValidCoordinates(coordinates)) {
1258                         setValueIndex(value, coordinates);
1259                     }
1260                 }
1261             }
1262             return this;
1263         }
1264 
1265         /**
1266          * Modifie la matrice actuel en metant les valeurs de mat passé en parametre
1267          * La copie se fait en fonction de la semantique, si un element dans une
1268          * dimension n'est pas trouvé, alors il est passé
1269          */
1270         public MatrixMap<E> paste(MatrixMap<E> mat) {
1271             if (mat != null) {
1272                 for (MatrixMapIterator<E> mi = mat.iterator(); mi.hasNext(); ) {
1273                     E value = mi.next();
1274                     Object[] sems = mi.getSemanticsCoordinates();
1275                     if (isValidCoordinates(sems)) {
1276                         setValue(value, sems);
1277                     }
1278                 }
1279             }
1280             return this;
1281         }
1282 
1283         /**
1284          * Permet de prendre une sous matrice dans la matrice courante. La sous
1285          * matrice a le même nombre de dimensions mais sur une des dimensions on ne
1286          * prend que certain élément.
1287          *
1288          * @param dim   la dimension dans lequel on veut une sous matrice si dim est
1289          *              négatif alors la dimension est prise à partir de la fin par
1290          *              exemple si l'on veut la derniere dimension il faut passer -1
1291          *              pour dim
1292          * @param start la position dans dim d'ou il faut partir pour prendre la
1293          *              sous matrice.
1294          * @param nb    le nombre d'élément à prendre dans la dimension. si nb est
1295          *              inférieur ou égal à 0 alors cela indique qu'il faut prendre
1296          *              tous les éléments jusqu'à la fin de la dimension.
1297          * @return new matrix
1298          */
1299         public MatrixMap<E> getSubMatrix(int dim, int start, int nb) {
1300             if (dim < 0) {
1301                 dim = getDimCount() + dim;
1302             }
1303             if (start < 0) {
1304                 start = getDim(dim) + start;
1305             }
1306             if (nb <= 0) {
1307                 nb = getDim(dim) - start;
1308             }
1309             return new SubMatrix<E>(this, dim, start, nb);
1310         }
1311 
1312         /**
1313          * Permet de prendre une sous matrice dans la matrice courante. La sous
1314          * matrice a le même nombre de dimensions mais sur une des dimensions on ne
1315          * prend que certain élément.
1316          *
1317          * @param dim   la dimension dans lequel on veut une sous matrice
1318          * @param start la position dans dim d'ou il faut partir pour prendre la
1319          *              sous matrice. 0 ≤ start &lt; dim.size si start est négatif alors
1320          *              la position de départ est calculé par rapport à la fin de la
1321          *              dimension, pour avoir le dernier élément il faut passer -1
1322          * @param nb    le nombre d'élément à prendre dans la dimension si nb est
1323          *              inférieur ou égal à 0 alors cela indique qu'il faut prendre
1324          *              tous les éléments jusqu'à la fin de la dimension.
1325          * @return new matrix
1326          */
1327         public MatrixMap<E> getSubMatrix(int dim, Object start, int nb) {
1328             int begin = MatrixHelper.indexOf(getSemantics(), dim, start);
1329             return getSubMatrix(dim, begin, nb);
1330         }
1331 
1332         /**
1333          * Add to desambiguas some call with xpath engine, but do the same thing
1334          * {@link #getSubMatrix(int, Object[])}
1335          *
1336          * @param dim  FIXME
1337          * @param elem FIXME
1338          * @return new matrix
1339          */
1340         public MatrixMap<E> getSubMatrixOnSemantic(int dim, Object... elem) {
1341             MatrixMap<E> result = getSubMatrix(dim, elem);
1342             return result;
1343         }
1344 
1345         /**
1346          * Permet de prendre une sous matrice dans la matrice courante. La sous
1347          * matrice a le même nombre de dimensions mais sur une des dimensions on ne
1348          * prend que certain élément.
1349          *
1350          * @param dim  la dimension dans lequel on veut une sous matrice
1351          * @param elem les éléments dans la dimension à conserver
1352          * @return new matrix
1353          */
1354         public MatrixMap<E> getSubMatrix(int dim, Object... elem) {
1355             int[] ielem = new int[elem.length];
1356             for (int i = 0; i < ielem.length; i++) {
1357                 ielem[i] = MatrixHelper.indexOf(getSemantics(), dim, elem[i]);
1358             }
1359             return getSubMatrix(dim, ielem);
1360         }
1361 
1362         /**
1363          * Permet de prendre une sous matrice dans la matrice courante.
1364          * <p>
1365          * Réalise plusieurs appels à {@link #getSubMatrix(int, Object...)} suivant
1366          * l'implémentation.
1367          *
1368          * @param elems les éléments dans la dimension à conserver
1369          * @return new matrix
1370          */
1371         public MatrixMap<E> getSubMatrix(Object[]... elems) {
1372 
1373             // la reduction doit se faire sur le meme nombre de dimension
1374             if (elems.length != dim.length) {
1375                 throw new IllegalArgumentException(String.format(
1376                         "Can't get sub matrix with different dimension count "
1377                                 + "(expected: %d, got %d)", dim.length, elems.length));
1378             }
1379 
1380             MatrixMap<E> result = this;
1381             for (int i = 0; i < elems.length; ++i) {
1382                 if (elems[i] != null) {
1383                     result = result.getSubMatrix(i, elems[i]);
1384                 }
1385             }
1386             return result;
1387         }
1388 
1389         /**
1390          * Permet de prendre une sous matrice dans la matrice courante. La sous
1391          * matrice a le même nombre de dimensions mais sur une des dimensions on ne
1392          * prend que certain élément.
1393          *
1394          * @param dim  la dimension dans lequel on veut une sous matrice
1395          * @param elem les indices des éléments dans la dimension à conserver
1396          * @return new matrix
1397          */
1398         public MatrixMap<E> getSubMatrix(int dim, int[] elem) {
1399             return new SubMatrix<E>(this, dim, elem);
1400         }
1401 
1402         /**
1403          * Permet de prendre une sous matrice dans la matrice courante.
1404          * <p>
1405          * Réalise plusieurs appels a {@link #getSubMatrix(int, int[])} suivant
1406          * l'implementation.
1407          *
1408          * @param elems les indices des éléments pour chaque dimension à conserver
1409          * @return new matrix
1410          */
1411         public MatrixMap<E> getSubMatrix(int[]... elems) {
1412 
1413             // la reduction doit se faire sur le meme nombre de dimension
1414             if (elems.length != dim.length) {
1415                 throw new IllegalArgumentException(String.format(
1416                         "Can't get sub matrix with different dimension count "
1417                                 + "(expected: %d, got %d)", dim.length, elems.length));
1418             }
1419 
1420             MatrixMap<E> result = this;
1421             for (int i = 0; i < elems.length; ++i) {
1422                 if (elems[i] != null) {
1423                     result = new SubMatrix<E>(result, i, elems[i]);
1424                 }
1425             }
1426             return result;
1427         }
1428 
1429         /**
1430          * Reduit la matrice de sorte que toutes les dimensions qui n'ont qu'un
1431          * élement soit supprimée. Au pire cette méthode retourne une matrice à une
1432          * seule dimension à un seul élément.
1433          *
1434          * @return une nouvelle matrice plus petite que la matrice actuelle ou egal
1435          * s'il n'y a aucune dimension à supprimer
1436          */
1437         public MatrixMap<E> reduce() {
1438             return reduce(1);
1439         }
1440 
1441         /**
1442          * Reduit le matrice seulement sur les dimensions passées en argument. Si
1443          * une des dimensions passées en arguement n'a pas qu'un seul élément, cette
1444          * dimension n'est pas prise en compte.
1445          *
1446          * @param dims les dimensions sur lequel il faut faire la reduction
1447          * @return une nouvelle matrice
1448          */
1449         public MatrixMap<E> reduceDims(int... dims) {
1450             Arrays.sort(dims);
1451             // tableau permettant de faire la correspondance entre les dimensions
1452             // de la matrice actuelle et les dimentsions de la nouvelle matrice
1453             // l'element i du tableau qui correcpond à la dimensions i de la
1454             // nouvelle matrice contient la dimension equivalente dans
1455             // la matrice actuelle
1456             int[] correspondance = new int[getDimCount()];
1457             // les nouvelles semantiques
1458             List<List> sem = new ArrayList<List>();
1459             // les nouveaux noms de dimensions
1460             List<String> dimName = new ArrayList<String>();
1461             // il faut au moins une dimension pour la matrice
1462             int minNbDim = 1;
1463             for (int j = getDimCount() - 1; j >= 0; j--) {
1464                 // si la dimension à plus d'un élément ou qu'il n'est pas dans dims
1465                 // on garde la dimension
1466                 if (getDim(j) > 1 || Arrays.binarySearch(dims, j) < 0
1467                         || j < minNbDim) {
1468                     // on ne conserve que les dimensions supérieure à 1
1469                     correspondance[sem.size()] = j;
1470                     sem.add(getSemantic(j));
1471                     dimName.add(getDimensionName(j));
1472                     minNbDim--;
1473                 }
1474             }
1475             MatrixMap<E> result = reduce(dimName, sem, correspondance);
1476             return result;
1477         }
1478 
1479         /**
1480          * Reduit la matrice de sorte que toutes les dimensions qui n'ont qu'un
1481          * élement soit supprimée. Au pire cette méthode retourne une matrice à une
1482          * seule dimension à un seul élément.
1483          *
1484          * @param minNbDim le nombre minimum de dimension que l'on souhaite pour la
1485          *                 matrice résultat
1486          * @return une nouvelle matrice plus petite que la matrice actuelle ou egal
1487          * s'il n'y a aucune dimension à supprimer
1488          */
1489         public MatrixMap<E> reduce(int minNbDim) {
1490             // tableau permettant de faire la correspondance entre les dimensions
1491             // de la matrice actuelle et les dimentsions de la nouvelle matrice
1492             // l'element i du tableau qui correcpond à la dimensions i de la
1493             // nouvelle matrice contient la dimension equivalente dans
1494             // la matrice actuelle
1495             int[] correspondance = new int[getDimCount()];
1496             // les nouvelles semantiques
1497             List<List> sem = new ArrayList<List>();
1498             // les nouveaux noms de dimensions
1499             List<String> dimName = new ArrayList<String>();
1500             for (int j = getDimCount() - 1; j >= 0; j--) {
1501                 // si la dimension à plus d'un élément ou si on a pas assez de
1502                 // dimension pour avoir le minimum demandé on prend la dimension
1503                 if (getDim(j) > 1 || j < minNbDim) {
1504                     // on ne conserve que les dimensions supérieure à 1
1505                     correspondance[sem.size()] = j;
1506                     sem.add(getSemantic(j));
1507                     dimName.add(getDimensionName(j));
1508                     // on vient de prendre une dimension il nous en faut une de
1509                     // moins
1510                     minNbDim--;
1511                 }
1512             }
1513 
1514             MatrixMap<E> result = reduce(dimName, sem, correspondance);
1515             return result;
1516         }
1517 
1518         /**
1519          * Create new matrice from the current matrix.
1520          *
1521          * @param dimName        dimension name for new matrix
1522          * @param sem            semantic for new matrix
1523          * @param correspondance array to do the link between current matrix and
1524          *                       returned matrix
1525          * @return new matrix
1526          */
1527         protected MatrixMap<E> reduce(List<String> dimName, List<List> sem, int[] correspondance) {
1528             // on converti les listes en tableau en inversant l'ordre car on
1529             // a fait un parcours en sens inverse
1530             int nbDim = sem.size();
1531             List[] newSemantics = new List[nbDim];
1532             String[] newDimNames = new String[nbDim];
1533             int[] tmpcorrespondance = new int[nbDim];
1534             for (int i = 0; i < nbDim; i++) {
1535                 newSemantics[i] = sem.get(nbDim - 1 - i);
1536                 newDimNames[i] = dimName.get(nbDim - 1 - i);
1537                 tmpcorrespondance[i] = correspondance[nbDim - 1 - i];
1538             }
1539             correspondance = tmpcorrespondance;
1540 
1541             MatrixMap<E> result = new MatrixMapFixed<E>(getName(), newDimNames, newSemantics);
1542 
1543             // on reprend les valeurs
1544             int[] newCoordinates = new int[result.getDimCount()];
1545             for (MatrixMapIterator<E> mi = iterator(); mi.hasNext(); ) {
1546                 E value = mi.next();
1547                 int[] oldCoordinates = mi.getCoordinates();
1548                 for (int i = 0; i < newCoordinates.length; i++) {
1549                     newCoordinates[i] = oldCoordinates[correspondance[i]];
1550                 }
1551                 result.setValueIndex(value, newCoordinates);
1552             }
1553             return result;
1554         }
1555 
1556         public MatrixMap<E> extend(Object... sems) {
1557             String name = getName();
1558             String[] dimNames = getDimensionNames();
1559             SemanticList[] semantics = getSemantics();
1560 
1561             // si pas assez de dimension on en rajoute
1562             if (sems.length > semantics.length) {
1563                 String[] newDimNames = new String[sems.length];
1564                 System.arraycopy(dimNames, 0, newDimNames, 0, dimNames.length);
1565                 dimNames = newDimNames;
1566 
1567                 SemanticList[] newSems = new SemanticList[sems.length];
1568                 System.arraycopy(semantics, 0, newSems, 0, semantics.length);
1569                 semantics = newSems;
1570 
1571                 for (int i = semantics.length; i < newSems.length; i++) {
1572                     newSems[i] = new SemanticList();
1573                 }
1574             }
1575 
1576             // si les objets demande n'existe pas dans la semantics on l'ajoute
1577             for (int i = 0; i < sems.length; i++) {
1578                 if (semantics[i].indexOf(sems[i]) == -1) {
1579                     semantics[i].add(sems[i]);
1580                 }
1581             }
1582 
1583             MatrixMap<E> result = MatrixMap.Factory.create(name, dimNames, semantics);
1584             result.paste(this);
1585             return result;
1586         }
1587 
1588     }
1589 
1590     /**
1591      * Pour l'instant une sous matrice a obligatoirement le meme nombre de dimension
1592      * que la matrice qu'elle contient. Elle permet juste de reduire le nombre
1593      * d'element d'une dimension.
1594      * <p>
1595      * C'est comme une "vue" réduite sur la vraie matrices.
1596      */
1597     class SubMatrix<E> extends AbstractMatrixMap<E> { // SubMatrix
1598 
1599         protected MatrixMap<E> matrix = null;
1600 
1601         protected DimensionConverter converter = null;
1602 
1603         public SubMatrix(MatrixMap<E> matrix, int dim, int start, int nb) {
1604             super(matrix.getName(), matrix.getDimensionNames(), matrix.getSemantics());
1605             this.matrix = matrix;
1606 
1607             converter = new ShiftConverter(dim, start, nb);
1608             setSemantic(dim, getSemantic(dim).subList(start, start + nb));
1609             getDim()[dim] = nb;
1610         }
1611 
1612         public SubMatrix(MatrixMap<E> matrix, int dim, int[] elem) {
1613             super(matrix.getName(), matrix.getDimensionNames(), matrix.getSemantics());
1614             this.matrix = matrix;
1615 
1616             converter = new MappingConverter(dim, elem);
1617 
1618             List oldSemantic = getSemantic(dim);
1619             List newSemantic = new LinkedList();
1620             for (int i = 0; i < elem.length; i++) {
1621                 newSemantic.add(oldSemantic.get(elem[i]));
1622             }
1623             setSemantic(dim, newSemantic);
1624             getDim()[dim] = elem.length;
1625         }
1626 
1627         @Override
1628         public MatrixMapIterator<E> iterator() {
1629             return new SubMatrixIterator<E>(this);
1630         }
1631 
1632         @Override
1633         public E getValueIndex(int... coordinates) {
1634             return matrix.getValueIndex(converter.convertCoordinates(coordinates));
1635         }
1636 
1637         @Override
1638         public void setValueIndex(E value, int... coordinates) {
1639             matrix.setValueIndex(value, converter.convertCoordinates(coordinates));
1640         }
1641 
1642         protected class SubMatrixIterator<E> implements MatrixMapIterator<E> {
1643 
1644             protected SubMatrix<E> subMatrix = null;
1645 
1646             protected int[] cpt = null;
1647 
1648             protected int[] last = null;
1649 
1650             public SubMatrixIterator(SubMatrix<E> subMatrix) {
1651                 this.subMatrix = subMatrix;
1652                 cpt = new int[subMatrix.getDimCount()];
1653                 cpt[cpt.length - 1] = -1;
1654 
1655                 last = new int[subMatrix.getDimCount()];
1656                 for (int i = 0; i < last.length; i++) {
1657                     last[i] = subMatrix.getDim(i) - 1;
1658                 }
1659 
1660             }
1661 
1662             @Override
1663             public boolean hasNext() {
1664                 return !Arrays.equals(cpt, last);
1665             }
1666 
1667             @Override
1668             public E next() {
1669                 int ret = 1;
1670                 int[] dim = getDim();
1671                 for (int i = cpt.length - 1; i >= 0; i--) {
1672                     cpt[i] = cpt[i] + ret;
1673                     ret = cpt[i] / dim[i];
1674                     cpt[i] = cpt[i] % dim[i];
1675                 }
1676                 E result = getValue();
1677                 return result;
1678             }
1679 
1680             @Override
1681             public void remove() {
1682                 setValue(null);
1683             }
1684 
1685             public int[] getCoordinates() {
1686                 return cpt;
1687             }
1688 
1689             public Object[] getSemanticsCoordinates() {
1690                 int[] coordinates = getCoordinates();
1691                 Object[] result = MatrixHelper.dimensionToSemantics(subMatrix.getSemantics(), coordinates);
1692                 return result;
1693             }
1694 
1695             public E getValue() {
1696                 return subMatrix.getValueIndex(getCoordinates());
1697             }
1698 
1699             public void setValue(E value) {
1700                 subMatrix.setValue(value, getCoordinates());
1701             }
1702         }
1703 
1704         /**
1705          * Permet de faire une conversion de la dimension demandé dans la sous
1706          * matrice avec la position reel de la matrice sous jacente.
1707          */
1708         protected interface DimensionConverter extends Serializable {
1709             int[] convertCoordinates(int[] coordinates);
1710         }
1711 
1712         /**
1713          * La conversion est juste un decalage d'indice
1714          */
1715         protected static class ShiftConverter implements DimensionConverter {
1716 
1717             /**
1718              * serialVersionUID.
1719              */
1720             private static final long serialVersionUID = 1L;
1721 
1722             protected int dim;
1723 
1724             protected int start;
1725 
1726             protected int nb;
1727 
1728             public ShiftConverter(int dim, int start, int nb) {
1729                 this.dim = dim;
1730                 this.start = start;
1731                 this.nb = nb;
1732             }
1733 
1734             @Override
1735             public int[] convertCoordinates(int[] coordinates) {
1736                 int[] result = null;
1737                 if (coordinates[dim] < nb) {
1738                     result = new int[coordinates.length];
1739                     System.arraycopy(coordinates, 0, result, 0, result.length);
1740                     result[dim] = result[dim] + start;
1741                 } else {
1742                     throw new NoSuchElementException(
1743                             "L'indice est supérieur au nombre d'élement de la sous matrice pour cette dimension.");
1744                 }
1745                 return result;
1746             }
1747         }
1748 
1749         /**
1750          * La conversion est le mapping d'un element vers un autre element.
1751          */
1752         protected static class MappingConverter implements DimensionConverter {
1753 
1754             /**
1755              * serialVersionUID.
1756              */
1757             private static final long serialVersionUID = -6367416559713556559L;
1758 
1759             protected int dim;
1760 
1761             protected int[] elem = null;
1762 
1763             public MappingConverter(int dim, int[] elem) {
1764                 this.dim = dim;
1765                 this.elem = new int[elem.length];
1766                 System.arraycopy(elem, 0, this.elem, 0, elem.length);
1767             }
1768 
1769             @Override
1770             public int[] convertCoordinates(int[] coordinates) {
1771                 int[] result = null;
1772                 if (coordinates[dim] < elem.length) {
1773                     result = new int[coordinates.length];
1774                     System.arraycopy(coordinates, 0, result, 0, result.length);
1775                     result[dim] = elem[coordinates[dim]];
1776 
1777                 } else {
1778                     throw new NoSuchElementException(
1779                             "L'indice est supérieur au nombre d'élements de la sous matrice pour cette dimension.");
1780                 }
1781                 return result;
1782             }
1783         }
1784     } // SubMatrix
1785 
1786     /**
1787      * Objet matrice qui ne permet que le stockage avec des positions int
1788      * dans une matrice a autant de dimension que l'on souhaite.
1789      *
1790      * @param <E> FIXME
1791      */
1792     class Matrix<E> implements Iterable<E> { // BasicMatrix
1793 
1794         /**
1795          * Les dimensions de la matrice
1796          */
1797         protected int[] dimensions = null;
1798 
1799         /**
1800          * La matrice en représentation linéaire
1801          */
1802         protected Vector<E> data = null;
1803 
1804         /**
1805          * tableau de facteur permettant de convertir les coordonnées dans la
1806          * matrice en un indice dans la représentation linéaire de la matrice
1807          */
1808         protected int[] linearFactor = null;
1809 
1810         /**
1811          * Crée une nouvelle matrice ayant les dimensions demandées.
1812          *
1813          * @param dimensions dimensions
1814          */
1815         public Matrix(int[] dimensions) {
1816             checkDim(dimensions);
1817 
1818             // copie des dimensions pour que personne à l'extérieur de l'objet
1819             // ne puisse les modifiers par la suite
1820             this.dimensions = new int[dimensions.length];
1821             System.arraycopy(dimensions, 0, this.dimensions, 0, dimensions.length);
1822 
1823             // calcul du linearFactor
1824             linearFactor = new int[dimensions.length];
1825             linearFactor[linearFactor.length - 1] = 1;
1826             for (int i = linearFactor.length - 2; i >= 0; i--) {
1827                 linearFactor[i] = linearFactor[i + 1] * dimensions[i + 1];
1828             }
1829 
1830             // creation de la matrice lineaire
1831             data = new Vector<E>(linearFactor[0] * dimensions[0]);
1832         }
1833 
1834         /**
1835          * Retourne le nombre de dimension de la matrice
1836          *
1837          * @return le nombre de dimension de la matrice;
1838          */
1839         public int getNbDim() {
1840             return dimensions.length;
1841         }
1842 
1843         /**
1844          * Retourne la taille d'une dimension
1845          *
1846          * @param dim la dimension dont on souhaite la taille
1847          * @return la taille d'une dimension
1848          */
1849         public int getDim(int dim) {
1850             checkDim(dim);
1851             return dimensions[dim];
1852         }
1853 
1854         /**
1855          * Retourne un tableau representant les dimensions de la matrice. Le tableau
1856          * retourné n'est pas une copie, il ne faut donc pas le modifier
1857          *
1858          * @return le tableau des dimensions.
1859          */
1860         public int[] getDim() {
1861             return dimensions;
1862         }
1863 
1864         /**
1865          * Retourne un element de la matrice
1866          *
1867          * @param pos la position de l'element à retourner
1868          * @return un element de la matrice
1869          */
1870         public E getValue(int[] pos) {
1871             int indice = coordonatesToLinear(pos);
1872             return data.getValue(indice);
1873         }
1874 
1875         /**
1876          * Modifie un élement de la matrice
1877          *
1878          * @param pos   la position de l'element à modifier
1879          * @param value la nouvelle valeur à mettre dans la matrice
1880          */
1881         public void setValue(int[] pos, E value) {
1882             int indice = coordonatesToLinear(pos);
1883             data.setValue(indice, value);
1884         }
1885 
1886         /**
1887          * Retourne un objet Inc pret a etre utilisé pour boucler sur tous les
1888          * element de la matrice.
1889          *
1890          * @return un objet Inc pret à être utilisé
1891          */
1892         @Override
1893         public MatrixIterator<E> iterator() {
1894             return new MatrixIterator<E>(this);
1895         }
1896 
1897         /**
1898          * Permet de faire un traitement sur chaque valeur de la matrice
1899          *
1900          * @param f la fonction a appliquer à chaque élement de la matrice
1901          */
1902         public void map(MapFunction f) {
1903             data.map(f);
1904         }
1905 
1906         /**
1907          * Permet de convertir les coordonnées d'un élément en un indice dans la
1908          * représentation linéraire de la matrice.
1909          *
1910          * @param coordonates les coordonnées à lineariser
1911          * @return un indice réprésentant les coordonnées de façon linéaire
1912          */
1913         protected int coordonatesToLinear(int[] coordonates) {
1914             checkPos(coordonates);
1915 
1916             int result = 0;
1917             for (int i = 0; i < linearFactor.length; i++) {
1918                 result += coordonates[i] * linearFactor[i];
1919             }
1920             return result;
1921         }
1922 
1923         /**
1924          * Convertie une coordonnée lineaire en coordonnées spaciales
1925          *
1926          * @param pos la coordonnée linéaire
1927          * @return les coordonnées spaciales de l'élément
1928          */
1929         protected int[] linearToCoordinates(int pos) {
1930             int[] result = new int[linearFactor.length];
1931 
1932             for (int i = 0; i < result.length; i++) {
1933                 result[i] = pos / linearFactor[i];
1934                 pos -= result[i] * linearFactor[i];
1935             }
1936             return result;
1937         }
1938 
1939         /**
1940          * Permet de vérifier que les dimensions de la nouvelle matrice sont
1941          * corrects
1942          *
1943          * @param dim les dimensions de la nouvelle matrice
1944          * @throws IllegalArgumentException si une dimension n'est pas valide
1945          */
1946         protected void checkDim(int[] dim) {
1947             for (int i = 0; i < dim.length; i++) {
1948                 if (dim[i] <= 0) {
1949                     throw new IllegalArgumentException(String.format(
1950                             "Dimension %s is invalid %s", i, dim[i]));
1951                 }
1952             }
1953         }
1954 
1955         /**
1956          * Permet de vérifier qu'une dimension demandé existe bien dans la matrice
1957          *
1958          * @param dim la position de la dimension que l'on souhaite
1959          * @throws IndexOutOfBoundsException si la dimension demandée n'existe pas
1960          */
1961         protected void checkDim(int dim) {
1962             if (dim < 0 || dim >= getNbDim()) {
1963                 throw new IndexOutOfBoundsException(String.format(
1964                         "Invalid dimension %s max dimension is %s",
1965                         dim, getNbDim()));
1966             }
1967         }
1968 
1969         /**
1970          * Verifie que les coordonnées demandé appartiennent bien à la matrice
1971          *
1972          * @param pos les coordonnées souhaitées dans la matrice
1973          * @throws NoSuchElementException si les coordonnées ne correspondent pas à
1974          *                                un élement de la matrice
1975          */
1976         protected void checkPos(int[] pos) {
1977             int[] dim = getDim();
1978             boolean result = dim.length == pos.length;
1979             for (int i = 0; result && i < dim.length; i++) {
1980                 result = (0 <= pos[i]) && (pos[i] < dim[i]);
1981             }
1982             if (!result) {
1983                 throw new NoSuchElementException(String.format(
1984                         "Invalid element asked %s for real dimension %s", Arrays.toString(pos), Arrays
1985                                 .toString(dim)));
1986             }
1987         }
1988 
1989         @Override
1990         public String toString() {
1991             StringBuffer result = new StringBuffer();
1992             if (getNbDim() == 1) {
1993                 result.append("matrix1D [");
1994                 for (int i = 0; i < data.size(); i++) {
1995                     result.append(data.getValue(i) + ",");
1996                 }
1997                 result.append("]");
1998             } else if (getNbDim() == 2) {
1999                 int[] pos = new int[2];
2000                 result.append("matrix2D [");
2001                 for (int y = 0; y < getDim(1); y++) {
2002                     result.append("\n");
2003                     for (int x = 0; x < getDim(0); x++) {
2004                         pos[0] = x;
2005                         pos[1] = y;
2006                         result.append(getValue(pos) + ",");
2007                     }
2008                 }
2009                 result.append("]");
2010             } else {
2011                 result.append("dimensions = [\n");
2012                 for (int i = 0; i < dimensions.length; i++) {
2013                     result.append(dimensions[i] + ",");
2014                 }
2015                 result.append("\n]\nmatrice = [\n");
2016                 for (int i = 0; i < data.size(); i++) {
2017                     result.append(data.getValue(i) + ",");
2018                 }
2019                 result.append("\n]\nlinearFactor = [\n");
2020                 for (int i = 0; i < linearFactor.length; i++) {
2021                     result.append(linearFactor[i] + ",");
2022                 }
2023                 result.append("\n]\n");
2024             }
2025             return result.toString();
2026         }
2027 
2028         @Override
2029         public boolean equals(Object o) {
2030             if (o instanceof Matrix) {
2031                 Matrix other = (Matrix) o;
2032                 return this == o
2033                         || (Arrays.equals(this.dimensions, other.dimensions) && this.data
2034                         .equals(other.data));
2035             }
2036             return false;
2037         }
2038 
2039     } // BasicMatrix
2040 
2041     class MatrixIterator<E> implements Iterator<E> { // MatrixIteratorImpl
2042 
2043         protected Matrix<E> matrix = null;
2044 
2045         protected int pos = -1;
2046 
2047         /**
2048          * @param matrix la matrice sur lequel l'iterator doit travailler
2049          */
2050         public MatrixIterator(Matrix<E> matrix) {
2051             this.matrix = matrix;
2052             pos = -1;
2053         }
2054 
2055         @Override
2056         public boolean hasNext() {
2057             return pos + 1 < matrix.data.size();
2058         }
2059 
2060         @Override
2061         public E next() {
2062             if (hasNext()) {
2063                 pos++;
2064             } else {
2065                 throw new NoSuchElementException();
2066             }
2067             E result = getValue();
2068             return result;
2069         }
2070 
2071         @Override
2072         public void remove() {
2073             setValue(null);
2074         }
2075 
2076         public E getValue() {
2077             return matrix.data.getValue(pos);
2078         }
2079 
2080         public void setValue(E value) {
2081             matrix.data.setValue(pos, value);
2082         }
2083 
2084         public int[] getCoordinates() {
2085             return matrix.linearToCoordinates(pos);
2086         }
2087 
2088     } // MatrixIteratorImpl
2089 
2090     /**
2091      * Permet de stocker des données à une position lineaire et de la redemander.
2092      * Cette classe ne gére que les données lineaire. L'avantage de cette classe est
2093      * de ne conserver que les elements differents de la valeur par defaut, ce qui
2094      * minimize la taille du tableau necessaire a conserver les données.
2095      *
2096      * @param <E> FIXME
2097      */
2098     class Vector<E> { // Vector
2099 
2100         /**
2101          * maximum number of element, maximum pos value
2102          */
2103         protected int capacity = 0;
2104 
2105         /**
2106          * la valeur par defaut
2107          */
2108         protected E defaultValue = null;
2109 
2110         /**
2111          * contient la position de l'element, le tableau est trie
2112          */
2113         protected int[] position;
2114 
2115         protected int positionSize = 0;
2116 
2117         /**
2118          * contient la valeur de l'element
2119          */
2120         protected ArrayList<E> data = new ArrayList<E>();
2121 
2122         public Vector(int capacity) {
2123             this.capacity = capacity;
2124             position = new int[8];
2125             Arrays.fill(position, Integer.MAX_VALUE);
2126         }
2127 
2128         public Vector(int capacity, E defaultValue) {
2129             this(capacity);
2130             this.defaultValue = defaultValue;
2131         }
2132 
2133         public int size() {
2134             return capacity;
2135         }
2136 
2137         // poussin 20060827 TODO: verifier l'implantation, il semble quelle soit
2138         // fausse et ne puisse pas recherche le nombre max correctement
2139         public E getMaxOccurrence() {
2140             E result = defaultValue;
2141 
2142             E[] tmp = (E[]) data.toArray();
2143 
2144             // si potentiellement il y a plus d'element identique dans data
2145             // que de valeur par defaut, on recherche la valeur possible
2146             if (this.capacity < 2 * tmp.length) {
2147                 Arrays.sort(tmp);
2148 
2149                 // le nombre de fois que l'on a rencontrer la valeur la plus
2150                 // nombreuse
2151                 int max = 1;
2152                 // le nombre de fois que l'on a rencontrer la valeur courante
2153                 int count = 1;
2154                 // la valeur la plus rencontrer
2155                 result = tmp[0];
2156                 // la valeur que l'on vient de traiter précédement
2157                 E old = tmp[0];
2158                 // la valeur courante lu dans le tableaux
2159                 E current = tmp[0];
2160                 // tant que l'on peut encore trouve un element plus nombreux dans le
2161                 // tableau on le parcours
2162                 for (int i = 1; max < tmp.length - i + count && i < tmp.length; i++) {
2163                     current = tmp[i];
2164 
2165                     if (current == old) {
2166                         count++;
2167                     } else {
2168                         if (count > max) {
2169                             max = count;
2170                             result = old;
2171                         }
2172                         count = 1;
2173                         old = current;
2174                     }
2175                 }
2176                 if (count > max) {
2177                     max = count;
2178                     result = current;
2179                 }
2180 
2181                 if (max <= capacity - tmp.length) {
2182                     // en fin de compte, il n'y a pas plus d'element identique
2183                     // dans data que de defaultValue
2184                     result = defaultValue;
2185                 }
2186             }
2187 
2188             return result;
2189         }
2190 
2191         protected void checkPos(int pos) {
2192             if (pos < 0 || pos >= capacity) {
2193                 throw new IllegalArgumentException("pos " + pos + " is not in [0, "
2194                         + capacity + "]");
2195             }
2196         }
2197 
2198         public E getValue(int pos) {
2199             checkPos(pos);
2200 
2201             E result = defaultValue;
2202             int index = findIndex(pos);
2203             if (index >= 0) {
2204                 result = data.get(index);
2205             }
2206             return result;
2207         }
2208 
2209         public void setValue(int pos, E value) {
2210             checkPos(pos);
2211 
2212             int index = findIndex(pos);
2213             if (index >= 0) {
2214                 if (value == defaultValue) {
2215                     // il etait present, on supprime l'element
2216                     removeElementAt(index);
2217                     data.remove(index);
2218                 } else {
2219                     // il etait deja present, on modifie la valeur
2220                     data.set(index, value);
2221                 }
2222             } else {
2223                 // il n'etait pas present
2224                 if (value != defaultValue) {
2225                     // il faut ajouter dans position et dans data
2226                     index = -index - 1;
2227 
2228                     addElementAt(index, pos);
2229                     data.add(index, value);
2230                 }
2231             }
2232         }
2233 
2234         public boolean equals(Object o) {
2235             boolean result = false;
2236             if (o instanceof Vector) {
2237                 Vector other = (Vector) o;
2238                 result = Arrays.equals(this.position, other.position)
2239                         && data.equals(other.data);
2240             }
2241             return result;
2242         }
2243 
2244         /**
2245          * retourne la position dans le tableau position de la position lineaire
2246          *
2247          * @param pos FIXME
2248          * @return la position ou &lt; 0 donnant la position de l'element s'il etait
2249          * present
2250          */
2251         protected int findIndex(int pos) {
2252             return Arrays.binarySearch(position, pos);
2253         }
2254 
2255         protected void ensureCapacity(int mincap) {
2256             if (mincap > position.length) {
2257                 int newcap = (position.length * 3) / 2 + 1;
2258                 int olddata[] = position;
2259                 position = new int[newcap >= mincap ? newcap : mincap];
2260                 System.arraycopy(olddata, 0, position, 0, positionSize);
2261                 for (int i = positionSize; i < position.length; i++) {
2262                     position[i] = Integer.MAX_VALUE;
2263                 }
2264             }
2265         }
2266 
2267         protected void addElementAt(int index, int element) {
2268             ensureCapacity(positionSize + 1);
2269             int numtomove = positionSize - index;
2270             System.arraycopy(position, index, position, index + 1, numtomove);
2271             position[index] = element;
2272             positionSize++;
2273         }
2274 
2275         protected int removeElementAt(int index) {
2276             int oldval = position[index];
2277             int numtomove = positionSize - index - 1;
2278             if (numtomove > 0) {
2279                 System.arraycopy(position, index + 1, position, index, numtomove);
2280             }
2281             positionSize--;
2282             position[positionSize] = Integer.MAX_VALUE;
2283             return oldval;
2284         }
2285 
2286         /**
2287          * On recopie tous les attributs pour que le vector ressemble exactement a
2288          * celui passé en argument
2289          * @param v FIXME
2290          */
2291         public void paste(Vector<E> v) {
2292             this.capacity = v.capacity;
2293             this.defaultValue = v.defaultValue;
2294             this.positionSize = v.positionSize;
2295             this.position = new int[v.position.length];
2296             System.arraycopy(v.position, 0, this.position, 0,
2297                     this.position.length);
2298             this.data.clear();
2299             this.data.addAll(v.data);
2300         }
2301 
2302         /**
2303          * on applique sur chaque donnée existante et sur default
2304          * @param f  FIXME
2305          */
2306         public void map(MapFunction<E> f) {
2307             // on commence toujours par modifier la valeur par defaut
2308             // car les valeurs suivante pourrait prendre cette valeur
2309             // et donc disparaitre des tableaux si besoin
2310             defaultValue = f.apply(defaultValue);
2311             // on fait la boucle a l'envers au cas ou on supprime des valeurs
2312             for (int i = data.size() - 1; i >= 0; i--) {
2313                 E value = f.apply(data.get(i));
2314                 if (value == defaultValue) {
2315                     // il etait present, on supprime l'element
2316                     removeElementAt(i);
2317                     data.remove(i);
2318                 } else {
2319                     // il etait deja present, on modifie la valeur
2320                     data.set(i, value);
2321                 }
2322             }
2323         }
2324     } // Vector
2325 
2326     /**
2327      * Permet de faire un traitement sur des valeurs et d'en retourner
2328      * des nouvelles.
2329      *
2330      * @param <E> FIXME
2331      */
2332     interface MapFunction<E> { // MapFunction
2333 
2334         /**
2335          * Permet de faire un traitement sur value et de retourne une nouvelle
2336          * valeur.
2337          *
2338          * @param value la valeur courante sur lequel il faut faire le traitement
2339          * @return la nouvelle valeur à mettre dans la matrice à la place de
2340          * l'ancienne.
2341          */
2342         E apply(E value);
2343 
2344     } // MapFunction
2345 
2346 }