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

com.hfg.util.collection.AbstractSparseMatrix Maven / Gradle / Ivy

There is a newer version: 20240423
Show newest version
package com.hfg.util.collection;

import java.io.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.hfg.util.CompareUtil;
import com.hfg.util.StringUtil;


//------------------------------------------------------------------------------
/**
 Base matrix class.
 
@author J. Alex Taylor, hairyfatguy.com
*/ //------------------------------------------------------------------------------ // com.hfg Library // // This library 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 2.1 of the License, or (at your option) any later version. // // This library 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 // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // J. Alex Taylor, President, Founder, CEO, COO, CFO, OOPS hairyfatguy.com // [email protected] //------------------------------------------------------------------------------ public abstract class AbstractSparseMatrix implements Cloneable { private LinkedHashSet mOrderedRowKeySet; private LinkedHashSet mOrderedColKeySet; private Map> mMatrixMap; private int mInitialColCapacity; private static int sDefaultInitialCapacity = 99; //########################################################################## // CONSTRUCTORS //########################################################################## //-------------------------------------------------------------------------- public AbstractSparseMatrix() { this(sDefaultInitialCapacity, sDefaultInitialCapacity); } //-------------------------------------------------------------------------- public AbstractSparseMatrix(int inInitialRowCapacity, int inInitialColCapacity) { mOrderedRowKeySet = new LinkedHashSet<>(inInitialRowCapacity); mOrderedColKeySet = new LinkedHashSet<>(inInitialColCapacity); mMatrixMap = new HashMap<>(inInitialRowCapacity); mInitialColCapacity = inInitialColCapacity; } //########################################################################## // PUBLIC METHODS //########################################################################## //-------------------------------------------------------------------------- public void addRow(RK inRowKey) { mOrderedRowKeySet.add(inRowKey); } //-------------------------------------------------------------------------- public void addCol(CK inColKey) { mOrderedColKeySet.add(inColKey); } //-------------------------------------------------------------------------- public void put(RK inRowKey, CK inColKey, V inValue) { mOrderedRowKeySet.add(inRowKey); mOrderedColKeySet.add(inColKey); Map colMap = mMatrixMap.get(inRowKey); if (null == colMap) { colMap = new HashMap<>(mInitialColCapacity); mMatrixMap.put(inRowKey, colMap); } colMap.put(inColKey, inValue); } //-------------------------------------------------------------------------- public void putRow(RK inRowKey, Map inColMap) { if (CollectionUtil.hasValues(inColMap)) { for (CK colKey : inColMap.keySet()) { put(inRowKey, colKey, inColMap.get(colKey)); } } } //-------------------------------------------------------------------------- public void putCol(CK inColKey, Map inRowMap) { if (CollectionUtil.hasValues(inRowMap)) { for (RK rowKey : inRowMap.keySet()) { put(rowKey, inColKey, inRowMap.get(rowKey)); } } } //-------------------------------------------------------------------------- public V get(RK inRowKey, CK inColKey) { V value = null; Map colMap = mMatrixMap.get(inRowKey); if (colMap != null) { value = colMap.get(inColKey); } return value; } //-------------------------------------------------------------------------- public V remove(RK inRowKey, CK inColKey) { V removedValue = null; Map colMap = mMatrixMap.get(inRowKey); if (colMap != null) { removedValue = colMap.remove(inColKey); } return removedValue; } //-------------------------------------------------------------------------- public void clearRow(RK inRowKey) { if (containsRow(inRowKey)) { for (CK colKey : colKeySet()) { remove(inRowKey, colKey); } } } //-------------------------------------------------------------------------- public void clearCol(CK inColKey) { if (containsCol(inColKey)) { for (RK rowKey : rowKeySet()) { remove(rowKey, inColKey); } } } //-------------------------------------------------------------------------- public void trimToSize() { LinkedHashSet orderedColKeySet = new LinkedHashSet<>(mOrderedColKeySet.size(), 1.0f); orderedColKeySet.addAll(mOrderedColKeySet); mOrderedColKeySet = orderedColKeySet; LinkedHashSet orderedRowKeySet = new LinkedHashSet<>(mOrderedRowKeySet.size(), 1.0f); orderedRowKeySet.addAll(mOrderedRowKeySet); mOrderedRowKeySet = orderedRowKeySet; // Not sure how this transfer can be done more efficiently Map> compactedMap = new HashMap<>(mMatrixMap.size(), 1.0f); compactedMap.putAll(mMatrixMap); mMatrixMap = compactedMap; for (RK rowKey : mMatrixMap.keySet()) { Map uncompactedValueMap = mMatrixMap.get(rowKey); if (uncompactedValueMap != null) { Map compactedValueMap = new HashMap<>(uncompactedValueMap.size(), 1.0f); compactedValueMap.putAll(uncompactedValueMap); mMatrixMap.put(rowKey, compactedValueMap); } } } //-------------------------------------------------------------------------- @Override public AbstractSparseMatrix clone() { AbstractSparseMatrix clone; try { clone = (AbstractSparseMatrix) super.clone(); } catch (CloneNotSupportedException e) { throw new RuntimeException(e); } clone.mOrderedRowKeySet = new LinkedHashSet<>(mOrderedRowKeySet); clone.mOrderedColKeySet = new LinkedHashSet<>(mOrderedColKeySet); clone.mMatrixMap = new HashMap<>(mMatrixMap.size()); for (RK rowKey : mMatrixMap.keySet()) { Map colMap = mMatrixMap.get(rowKey); if (colMap != null) { Map clonedColMap = new HashMap<>(colMap.size()); for (CK colKey : colMap.keySet()) { clonedColMap.put(colKey, colMap.get(colKey)); } clone.mMatrixMap.put(rowKey, clonedColMap); } else { clone.mMatrixMap.put(rowKey, null); } } return clone; } //-------------------------------------------------------------------------- public int size() { int size = 0; for (Map colMap : mMatrixMap.values()) { if (colMap != null) { size += colMap.size(); } } return size; } //-------------------------------------------------------------------------- public int rowCount() { return mOrderedRowKeySet != null ? mOrderedRowKeySet.size() : 0; } //-------------------------------------------------------------------------- public MatrixCell getCellWithSmallestValue() { return getCellWithSmallestValue(true); } //-------------------------------------------------------------------------- public MatrixCell getNonIdentityCellWithSmallestValue() { return getCellWithSmallestValue(false); } //-------------------------------------------------------------------------- // The matrix could be large. Don't try to dump the whole thing out in toString() // as this can be used in the debugger. @Override public String toString() { return (mOrderedRowKeySet != null ? mOrderedRowKeySet.size() : "0") + " x " + (mOrderedColKeySet != null ? mOrderedColKeySet.size() : "0") + " matrix"; } //-------------------------------------------------------------------------- public String toASCII() { String outString; try { ByteArrayOutputStream outStream = new ByteArrayOutputStream(); toASCII(outStream); outStream.close(); outString = outStream.toString(); } catch (IOException e) { throw new RuntimeException(e); } return outString; } //-------------------------------------------------------------------------- public void toASCII(OutputStream inStream) throws IOException { Writer writer = new PrintWriter(inStream); int maxRowKeyLength = getMaxRowKeyLength(); // Build a map of columm string lengths Map maxColValueLengthMap = new HashMap<>(colKeySet().size()); for (CK colKey : colKeySet()) { Integer maxColValueLength = null; for (RK rowKey : rowKeySet()) { V value = get(rowKey, colKey); if (value != null) { String stringValue = getValueString(value); if (null == maxColValueLength || stringValue.length() > maxColValueLength) { maxColValueLength = stringValue.length(); } } } maxColValueLengthMap.put(colKey, maxColValueLength); } // Header line writer.write(StringUtil.polyChar(' ', maxRowKeyLength) + " "); for (CK colKey : colKeySet()) { int colKeyLength = colKey.toString().length(); Integer maxColValueLength = maxColValueLengthMap.get(colKey); if (null == maxColValueLength || colKeyLength > maxColValueLength) { maxColValueLength = colKeyLength; } writer.write(String.format("%" + maxColValueLength + "." + maxColValueLength + "s ", colKey.toString())); } writer.write("\n"); for (RK rowKey : rowKeySet()) { writer.write(String.format("%" + maxRowKeyLength + "." + maxRowKeyLength + "s ", rowKey.toString())); for (CK colKey : colKeySet()) { int colKeyLength = colKey.toString().length(); Integer maxColValueLength = maxColValueLengthMap.get(colKey); if (null == maxColValueLength || colKeyLength > maxColValueLength) { maxColValueLength = colKeyLength; } V value = get(rowKey, colKey); if (value != null) { String stringValue = getValueString(value); writer.write(String.format("%" + maxColValueLength + "." + maxColValueLength + "s ", stringValue)); } else { writer.write(StringUtil.polyChar(' ', maxColValueLength) + " "); } } writer.write("\n"); } writer.flush(); } //-------------------------------------------------------------------------- private String getValueString(V inValue) { String stringValue; if (inValue instanceof Double || inValue instanceof Float) { stringValue = String.format("%.2f", inValue); } else { stringValue = inValue.toString(); } return stringValue; } //########################################################################## // PROTECTED METHODS //########################################################################## //-------------------------------------------------------------------------- protected Set rowKeySet() { return Collections.unmodifiableSet(mOrderedRowKeySet); } //-------------------------------------------------------------------------- protected boolean containsRow(RK inRowKey) { return mOrderedRowKeySet.contains(inRowKey); } //-------------------------------------------------------------------------- protected Map removeRow(RK inRowKey) { mOrderedRowKeySet.remove(inRowKey); return mMatrixMap.remove(inRowKey); } //-------------------------------------------------------------------------- /** Changes the rowkey inOldKey to inNewKey. */ protected void changeRowKey(RK inOldKey, RK inNewKey) { if (mOrderedRowKeySet != null) { List rowKeyList = new ArrayList<>(mOrderedRowKeySet); int index = rowKeyList.indexOf(inOldKey); if (index >= 0) { rowKeyList.remove(inOldKey); rowKeyList.add(index, inNewKey); mOrderedRowKeySet = new LinkedHashSet<>(rowKeyList); mMatrixMap.put(inNewKey, mMatrixMap.remove(inOldKey)); } } } //-------------------------------------------------------------------------- protected Set colKeySet() { return Collections.unmodifiableSet(mOrderedColKeySet); } //-------------------------------------------------------------------------- protected Map getRow(RK inRowKey) { Map colMap = mMatrixMap.get(inRowKey); return (colMap != null ? Collections.unmodifiableMap(colMap) : null); } //-------------------------------------------------------------------------- protected Map getCol(CK inColKey) { Map rowMap = null; if (containsCol(inColKey)) { rowMap = new OrderedMap<>(rowKeySet().size()); for (RK rowKey : rowKeySet()) { Map rowData = mMatrixMap.get(rowKey); if (rowData != null) { rowMap.put(rowKey, rowData.get(inColKey)); } } } return rowMap; } //-------------------------------------------------------------------------- protected boolean containsCol(CK inColKey) { return mOrderedColKeySet.contains(inColKey); } //-------------------------------------------------------------------------- protected void removeCol(CK inColKey) { if (mOrderedColKeySet.remove(inColKey)) { for (Map colMap : mMatrixMap.values()) { colMap.remove(inColKey); } } } //-------------------------------------------------------------------------- protected void removeCols(Set inColKeys) { if (inColKeys != null && mOrderedColKeySet.removeAll(inColKeys)) { for (Map colMap : mMatrixMap.values()) { for (CK colKey : inColKeys) { colMap.remove(colKey); } } } } //-------------------------------------------------------------------------- /** Changes the colkey inOldKey to inNewKey. */ protected void changeColKey(CK inOldKey, CK inNewKey) { if (mOrderedColKeySet != null) { List colKeyList = new ArrayList<>(mOrderedColKeySet); int index = colKeyList.indexOf(inOldKey); if (index >= 0) { colKeyList.remove(inOldKey); colKeyList.add(index, inNewKey); mOrderedColKeySet = new LinkedHashSet<>(colKeyList); for (RK rowKey : mMatrixMap.keySet()) { Map colMap = mMatrixMap.get(rowKey); if (colMap != null) { if (colMap.containsKey(inOldKey)) { colMap.put(inNewKey, colMap.remove(inOldKey)); } } } } } } //-------------------------------------------------------------------------- private int getMaxRowKeyLength() { int maxLength = 0; if (CollectionUtil.hasValues(mOrderedRowKeySet)) { for (RK key : mOrderedRowKeySet) { if (key != null) { int keyLength = key.toString().length(); if (keyLength > maxLength) { maxLength = keyLength; } } } } return maxLength; } //-------------------------------------------------------------------------- private int getMaxColKeyLength() { int maxLength = 0; if (CollectionUtil.hasValues(mOrderedColKeySet)) { for (CK key : mOrderedColKeySet) { if (key != null) { int keyLength = key.toString().length(); if (keyLength > maxLength) { maxLength = keyLength; } } } } return maxLength; } //-------------------------------------------------------------------------- private MatrixCell getCellWithSmallestValue(boolean inIncludeIdentityCells) { V smallestValue = null; RK targetRow = null; CK targetCol = null; for (RK rowKey : mMatrixMap.keySet()) { Map colMap = mMatrixMap.get(rowKey); if (colMap != null) { for (CK colKey : colMap.keySet()) { if (inIncludeIdentityCells || ! rowKey.equals(colKey)) { V value = colMap.get(colKey); if (value != null) { int comparison = 0; if (null == smallestValue) { comparison = 1; } else { comparison = CompareUtil.compare(smallestValue, value); if (0 == comparison) { comparison = CompareUtil.compare(targetCol.toString(), colKey.toString()); } if (0 == comparison) { comparison = CompareUtil.compare(targetRow.toString(), rowKey.toString()); } } if (1 == comparison) { smallestValue = value; targetRow = rowKey; targetCol = colKey; } } } } } } // System.out.println(smallestValue + " ( " + targetRow + " " + targetCol); return smallestValue != null ? new MatrixCell<>(targetRow, targetCol, smallestValue) : null; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy