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

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

The newest version!
/*
 * #%L
 * Nuiton Matrix :: API
 * %%
 * Copyright (C) 2004 - 2022 CodeLutin
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as 
 * published by the Free Software Foundation, either version 3 of the 
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Lesser Public License for more details.
 * 
 * You should have received a copy of the GNU General Lesser Public 
 * License along with this program.  If not, see
 * .
 * #L%
 */

package org.nuiton.math.matrix;

import java.io.StreamTokenizer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Stack;

/**
 * Contains usefull methods to get information on matrix.
 *
 * Created: 28 oct. 2004
 *
 * @author Benjamin Poussin <[email protected]>
 * @version $Revision$
 *
 * Mise a jour: $Date$
 * par : $Author$
 */
public class MatrixHelper { // MatrixHelper


    public static boolean isValidCoordinates(int[] dim, int[] pos) {
        boolean result = dim.length == pos.length;
        for (int i = 0; result && i < pos.length; i++) {
            result = 0 <= pos[i] && pos[i] < dim[i];
        }
        return result;
    }

    public static boolean isValidCoordinates(List[] semantics, Object[] pos) {
        boolean result = semantics.length == pos.length;
        for (int i = 0; result && i < pos.length; i++) {
            List semantic = semantics[i];
            result = semantic.contains(pos[i]);
        }
        return result;
    }

    /**
     * Retourne le linearFactor, ce tableau sert a convertir une position donnee
     * par les dimensions d'une matrice en une position dans un Vecteur.
     * @param dimensions
     * @return
     */
    public static long[] getLinearFactor(int[] dimensions) {
        // calcul du linearFactor
        long[] linearFactor = new long[dimensions.length];
        linearFactor[linearFactor.length - 1] = 1;
        for (int i = linearFactor.length - 2; i >= 0; i--) {
            linearFactor[i] = linearFactor[i + 1] * dimensions[i + 1];
        }
        return linearFactor;
    }

    /**
     * Retourne la taille que doit avoir un vecteur pour etre utilise avec
     * les dimensions et le linearFactor en argument
     * @param dimensions
     * @param linearFactor calculer prealablement par getLinearFactor
     * @return
     */
    public static long getVectorSize(int[] dimensions, long[] linearFactor) {
        long result = linearFactor[0] * dimensions[0];
        return result;
    }

    /**
     * Retourne la taille que doit avoir un vecteur pour etre utilise avec
     * les dimensions en argument
     * @param dimensions
     * @return
     */
    public static long getVectorSize(int[] dimensions) {
        long result = getLinearFactor(dimensions)[0] * dimensions[0];
        return result;
    }

    /**
     * Convert Matrix to identity matrix must have 2 dimensions. If dimension
     * haven't same length, then the small dimension is used.
     * 
     * @param mat matrix nd to convert
     * @return converted matrix
     */
    public static MatrixND convertToId(MatrixND mat) {
        int size = mat.getDim(0);
        if (size > mat.getDim(1)) {
            size = mat.getDim(1);
        }

        fill(mat, 0);

        for (int i = 0; i < size; i++) {
            mat.setValue(i, i, 1);
        }
        return mat;
    }

    /**
     * Permet de relire une chaine du type [[[1, 2], [3, 4]],[[3, 5], [1, 4]]]
     * 

* Remarque: une premiere implantantion avait ete faite en utilisant * {@link StreamTokenizer} mais en fait il y a un bug dedans, il ne sait pas * parser les chiffres avec un exposant: 5.0E-7 par exemple est lu comme 5.0 * :( *

* Remarque: une autre implantation de remplacement a ete faite en utilisant * le {@link org.nuiton.util.StringUtil#split(String, String)} mais elle * etait moins performante (x2) * * @param s la chaine representant les listes de liste * @return une liste de liste ... de Double */ public static List convertStringToList(String s) { List result = null; Stack> stack = new Stack>(); StringBuilder number = new StringBuilder(20); // initial to 20 char for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); if (c == ' ') { // skip space } if (c == '[') { stack.push(new ArrayList()); } else if (c == ',') { if (number.length() != 0) { // on a une ',' on doit donc avoir un nombre dans number // a moins que ce ne soit une ',' entre deux listes Double value = Double.valueOf(number.toString()); stack.peek().add(value); } number.setLength(0); } else if (c == ']') { // fin d'une liste, il doit rester un nombre dans number // a moins que la liste etait vide if (number.length() != 0) { Double value = Double.valueOf(number.toString()); stack.peek().add(value); number.setLength(0); } List current = stack.pop(); if (stack.empty()) { result = current; } else { stack.peek().add(current); } } else { // pas un '[' ou ']', pas une ',' devrait etre // un bout du nombre, si c un espace qui traine n'importe ou // c pas grave car Double.valueOf gere les espaces number.append(c); } } return result; } /** * permet de donner une représentation String d'un tableau de coordonnées * * @param coordinates les coordonnées * @return la chaine demandée de la forme 1,3,34,23 */ public static String coordinatesToString(int[] coordinates) { StringBuffer sb = new StringBuffer(); for (int i = 0; i < coordinates.length; i++) { sb.append(coordinates[i]); if (i + 1 < coordinates.length) { sb.append(','); } } return sb.toString(); } /** * permet de donner une représentation String d'un tableau de coordonnées * * @param coordinates les coordonnées * @return la chaine demandée de la forme "Ob1","Ob2,"Ob3", ... la chaine * prise pour l'objet est celle retournée par la méthode toString de * l'objet */ public static String coordinatesToString(Object[] coordinates) { StringBuffer sb = new StringBuffer(); for (int i = 0; i < coordinates.length; i++) { sb.append(coordinates[i]); if (i + 1 < coordinates.length) { sb.append(','); } } return sb.toString(); } /** * Permet de savoir si deux dimension sont identiques. * * @param dim1 first dimensions * @param dim2 second dimensions * @return dimension equality */ public static boolean sameDimension(int[] dim1, int[] dim2) { return Arrays.equals(dim1, dim2); } public static boolean sameSemantics(List[] sems1, List[] sems2) { // Arrays.equals and List.equals do the jobs boolean result = Arrays.equals(sems1, sems2); return result; } /** * Permet de convertir des coordonnées définies par des entiers en coordonnées * semantique par des objets * * @param result le tableau utilise pour stocker le resultat. Force sa creation * par l'appelant permet de ne pas creer un tableau pour chaque appel, l'appelant * peu utiliser a chaque fois le meme. * @param semantics la semantique à utilisé pour la conversion * @param coordinates les coordonnées à convertir * @return un tableau donnant les coordonnées sous forme semantique s'il n'y * a pas de semantique (liste pleine de null) alors un objet Integer * est créer pour représenter la semantique de la dimension. */ public static Object[] dimensionToSemantics(Object[] result, List[] semantics, int[] coordinates) { for (int i = 0; i < result.length; i++) { result[i] = semantics[i].get(coordinates[i]); if (result[i] == null) { result[i] = MatrixDimensionIndex.valueOf(coordinates[i]); } } return result; } /** * Permet de convertir des coordonnées sémantiques en coordonnées défini par * des entiers. Cette fonction est l'inverse de * {@link #dimensionToSemantics}. * * @param semantics la semantique à utiliser pour la conversion * @param coordinates les coordonnées sémantique * @return les coordonnées en entier. Si la sémantique est représentée par un * Integer alors la valeur de l'integer est utilisé pour la * conversion. */ public static int[] semanticsToDimension(List[] semantics, Object[] coordinates) { int[] result = new int[coordinates.length]; for (int i = 0; i < coordinates.length; i++) { if (coordinates[i] == null) { result[i] = -1; } else { result[i] = indexOf(semantics, i, coordinates[i]); } } return result; } /** * Permet de retrouver la position d'un objet dans une liste * * @param semantics la semantique à utilisé pour la recherche * @param dim la dimension dans lequel il faut faire la recherche * @param o l'objet à rechercher * @return la position de l'objet dans la dimension demandée * * @throws NoSuchElementException If element doesn't exists */ public static int indexOf(List[] semantics, int dim, Object o) throws NoSuchElementException { // see {#dimensionToSemantics()} to known why if (o instanceof MatrixDimensionIndex) { return ((MatrixDimensionIndex) o).intValue(); } int result = -1; if ((0 <= dim) && (dim < semantics.length)) { result = semantics[dim].indexOf(o); } if (result == -1) { throw new NoSuchElementException( "L'objet passé en argument n'a pas été retrouvé ou la dimension donnée ne convient pas:" + o + " in " + semantics[dim]); } return result; } /** * Permet de remplir toute la matrice avec la même donnée * * @param mat la matrice à remplir * @param value la valeur de remplissage * @return la matrice passé en paramêtre */ public static MatrixND fill(MatrixND mat, final double value) { mat.map(new MapFunction() { @Override public double apply(double v) { return value; } }); return mat; } /** * Retourne la valeur la plus courrement rencontrer dans un tableau. si * plusieurs valeurs ont le même nombre d'occurrence la plus petite valeur * est retournée. * * @param tab le tableau de valeur * @return la valeur la plus nombreuse dans le tableau * * @deprecated since 2.1, use {@link #maxOccurrence(double[])} instead */ @Deprecated public static double maxOccurence(double[] tab) { return maxOccurrence(tab); } /** * Retourne la valeur la plus courrement rencontrer dans un tableau. si * plusieurs valeurs ont le même nombre d'occurrence la plus petite valeur * est retournée. * * @param tab le tableau de valeur * @return la valeur la plus nombreuse dans le tableau * * @deprecated since 2.1, use {@link #maxOccurrence(float[])} instead */ @Deprecated public static double maxOccurence(float[] tab) { return maxOccurrence(tab); } /** * Retourne la valeur la plus courrement rencontrer dans un tableau. si * plusieurs valeurs ont le même nombre d'occurrence la plus petite valeur * est retournée. * * @param tab le tableau de valeur * @return la valeur la plus nombreuse dans le tableau */ public static double maxOccurrence(double[] tab) { double[] tmp = new double[tab.length]; System.arraycopy(tab, 0, tmp, 0, tab.length); return maxOccurrence1(tmp); } /** * Retourne la valeur la plus courrement rencontrer dans un tableau. si * plusieurs valeurs ont le même nombre d'occurrence la plus petite valeur * est retournée. * * @param tab le tableau de valeur * @return la valeur la plus nombreuse dans le tableau */ public static double maxOccurrence(float[] tab) { double[] tmp = new double[tab.length]; for (int i = 0; i < tab.length; i++) { tmp[i] = tab[i]; } return maxOccurrence1(tmp); } /** * le tableau en entre est trie durant l'execution de la methode, il est * donc modifié * @param tmp TODO * @return TODO */ protected static double maxOccurrence1(double[] tmp) { if (tmp.length == 0) { throw new IllegalArgumentException("Array must be not empty"); } // double [] tmp = new double[tab.length]; // System.arraycopy(tab, 0, tmp, 0, tab.length); Arrays.sort(tmp); // le nombre de fois que l'on a rencontrer la valeur la plus nombreuse int max = 1; // le nombre de fois que l'on a rencontrer la valeur courante int count = 1; // la valeur la plus rencontrer double result = tmp[0]; // la valeur que l'on vient de traiter précédement double old = tmp[0]; // la valeur courante lu dans le tableaux double current = tmp[0]; // tant que l'on peut encore trouve un element plus nombreux dans le // tableau on le parcours for (int i = 1; max < tmp.length - i + count && i < tmp.length; i++) { current = tmp[i]; if (current == old) { count++; } else { if (count > max) { max = count; result = old; } count = 1; old = current; } } if (count > max) { max = count; result = current; } return result; } } // MatrixHelper /** * This class must stay private and can't be used outside nuiton matrix * to prevent semantic type collision. * * This class is a copy of JDK's Integer with only necessary stuff. */ class MatrixDimensionIndex { protected int value; private MatrixDimensionIndex(int value) { this.value = value; } /** * See {@code Integer#IntegerCache} for details about that. */ private static class MatrixDimensionIndexCache { static final int high; static final MatrixDimensionIndex cache[]; static { final int low = 0; // high value may be configured by property high = 127; cache = new MatrixDimensionIndex[(high - low) + 1]; int j = low; for(int k = 0; k < cache.length; k++) { cache[k] = new MatrixDimensionIndex(j++); } } private MatrixDimensionIndexCache() {} } public static MatrixDimensionIndex valueOf(int i) { if (i >= 0 && i <= MatrixDimensionIndexCache.high) { return MatrixDimensionIndexCache.cache[i]; } else { return new MatrixDimensionIndex(i); } } public int intValue() { return value; } }