Java tutorial
/* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ package local.laer.app.knapsack.support; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.type.TypeFactory; import com.fasterxml.jackson.databind.util.Converter; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Objects; import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; import static java.util.stream.Collectors.toList; import local.laer.app.knapsack.domain.Cell; import local.laer.app.knapsack.domain.ImmutablePosition; import local.laer.app.knapsack.domain.support.Matrix; import local.laer.app.knapsack.domain.Position; import static local.laer.app.knapsack.support.IntFunctions.col; import static local.laer.app.knapsack.support.IntFunctions.partial; import local.laer.app.knapsack.support.matrix.BoolMatrix; import local.laer.app.knapsack.support.matrix.IntMatrix; /** * * @author Lars Eriksson (larsq.eriksson@gmail.com) */ @JsonSerialize(converter = OffsetBinaryMatrix.JsonConverter.class) public class OffsetBinaryMatrix implements BoolMatrix, IntMatrix { public static class JsonConverter implements Converter<OffsetBinaryMatrix, Matrix> { private static Matrix.Stripe defineRowStripe(OffsetBinaryMatrix matrix, int row) { Matrix.Stripe stripe = new Matrix.Stripe(); stripe.setDirection(Matrix.Direction.Row); stripe.setOffset(new ImmutablePosition(row, matrix.colOffset)); int[] rowData = matrix.extractRow(matrix.colOffset, matrix.cols(), row); String[] converted = Arrays.stream(rowData).mapToObj(String::valueOf).collect(toList()) .toArray(new String[0]); stripe.setData(converted); return stripe; } @Override public JavaType getInputType(TypeFactory typeFactory) { return typeFactory.constructType(OffsetBinaryMatrix.class); } @Override public JavaType getOutputType(TypeFactory typeFactory) { return typeFactory.constructType(Matrix.class); } @Override public Matrix convert(OffsetBinaryMatrix value) { List<Matrix.Stripe> stripes = new ArrayList<>(); value.consume( partial(col(value.colOffset), cell -> stripes.add(defineRowStripe(value, cell.getRow())))); Matrix m = new Matrix(); m.setContent(stripes.toArray(new Matrix.Stripe[stripes.size()])); m.setDefaultValue(0); return m; } } protected static boolean within(int val, int lowerInclusive, int upperInclusive) { return lowerInclusive <= val && val <= upperInclusive; } protected static boolean[][] copy(boolean[][] value) { boolean[][] clone = new boolean[value.length][value[0].length]; for (int i = 0; i < clone.length; i++) { System.arraycopy(value[i], 0, clone[i], 0, value[i].length); } return clone; } public static OffsetBinaryMatrix fillWith(OffsetBinaryMatrix matrix, boolean value) { return matrix.transform(IntFunctions.booleanFunction(__ -> value)); } private final int colOffset; private final int rowOffset; private final boolean[][] value; public OffsetBinaryMatrix(int row, int col, int rows, int cols) { value = new boolean[rows][cols]; this.colOffset = col; this.rowOffset = row; } protected OffsetBinaryMatrix(int newRow, int newCol, boolean[][] value) { this.colOffset = newCol; this.rowOffset = newRow; this.value = OffsetBinaryMatrix.this.copy(value); } public OffsetBinaryMatrix copy() { return new OffsetBinaryMatrix(rowOffset, colOffset, value); } public OffsetBinaryMatrix translateTo(Position position) { return translate(position.getRow(), position.getCol()); } public OffsetBinaryMatrix translate(int row, int col) { return new OffsetBinaryMatrix(row, col, value); } public OffsetBinaryMatrix transform(Function<Cell<Integer>, Integer> callback) { OffsetBinaryMatrix matrix = new OffsetBinaryMatrix(rowOffset, colOffset, value); matrix.mutableTransform(callback); return matrix; } public void consume(Consumer<Cell<Integer>> callback) { SimpleCell<Integer> cell = new SimpleCell<>(); for (int row = 0; row < rows(); row++) { for (int col = 0; col < cols(); col++) { cell.col = col + colOffset; cell.row = row + rowOffset; cell.value = convertToInt(value[row][col]); callback.accept(cell); } } } protected int[] extractCol(int lowerRowIndexInclusive, int nRows, int col) { int[] data = new int[nRows]; for (int i = 0; i < nRows; i++) { data[i] = getInt(i + lowerRowIndexInclusive, col); } return data; } protected int[] extractRow(int lowerColIndexInclusive, int nCols, int row) { int[] data = new int[nCols]; for (int i = 0; i < nCols; i++) { data[i] = getInt(row, i + lowerColIndexInclusive); } return data; } protected static Integer convertToInt(Boolean b) { if (b == null) { return 0; } return b ? 1 : 0; } protected static Boolean convertToBool(Integer i) { if (i == null) { return false; } return !Objects.equals(i, 0); } protected void mutableTransform(Function<Cell<Integer>, Integer> callback) { SimpleCell<Integer> cell = new SimpleCell<>(); for (int row = 0; row < rows(); row++) { for (int col = 0; col < cols(); col++) { cell.col = col + colOffset; cell.row = row + rowOffset; cell.value = convertToInt(value[row][col]); Integer newValue = callback.apply(cell); value[row][col] = convertToBool((newValue == null) ? 0 : newValue); } } } public boolean matchAll(Predicate<Cell<Integer>> callback) { SimpleCell cell = new SimpleCell(); for (int row = 0; row < rows(); row++) { for (int col = 0; col < cols(); col++) { cell.col = col; cell.row = row; cell.value = convertToInt(value[row][col]); if (!callback.test(cell)) { return false; } } } return true; } boolean relative(int row, int col) { if (rows() <= row) { return false; } if (cols() <= col) { return false; } return value[row][col]; } @Override public int getInt(int row, int col) { return getBool(row, col) ? 1 : 0; } protected int lowerCol() { return colOffset; } protected int lowerRow() { return rowOffset; } protected int cols() { return value[0].length; } protected int rows() { return value.length; } protected int upperColIndexInclusive() { return colOffset + cols() - 1; } protected int upperRowIndexInclusive() { return rowOffset + rows() - 1; } public boolean defines(int row, int col) { return within(col, lowerCol(), upperColIndexInclusive()) && within(row, lowerRow(), upperRowIndexInclusive()); } @Override public boolean getBool(int row, int col) { if (!defines(row, col)) { return false; } return value[row - lowerRow()][col - lowerCol()]; } public OffsetBinaryMatrix intersection(OffsetBinaryMatrix otherMatrix, Mode mode) { if (!intersectsWith(otherMatrix)) { return null; } int lowerX = Math.max(lowerCol(), otherMatrix.lowerCol()); int lowerY = Math.max(lowerRow(), otherMatrix.lowerRow()); int upperX = Math.min(upperColIndexInclusive(), otherMatrix.upperColIndexInclusive()); int upperY = Math.min(upperRowIndexInclusive(), otherMatrix.upperRowIndexInclusive()); int cols = upperX - lowerX + 1; int rows = upperY - lowerY + 1; OffsetBinaryMatrix matrix = new OffsetBinaryMatrix(lowerY, lowerX, rows, cols); for (int col = 0; col < cols; col++) { for (int row = 0; row < rows; row++) { boolean val = getBool(lowerY + row, lowerX + col); boolean otherVal = otherMatrix.getBool(lowerY + row, lowerX + col); matrix.value[row][col] = mode.apply(val, otherVal); } } return matrix; } public boolean zero() { for (boolean[] row : value) { for (boolean col : row) { if (col == true) { return false; } } } return true; } public boolean one() { for (boolean[] row : value) { for (boolean col : row) { if (col == false) { return false; } } } return true; } private int size() { return value.length * value[0].length; } public int sum() { int sum = 0; for (boolean[] row : value) { for (boolean col : row) { if (col == true) { sum += 1; } } } return sum; } public double ratio() { return ((double) sum()) / ((double) size()); } public boolean intersectsWith(OffsetBinaryMatrix otherMatrix) { if (otherMatrix == this) { return true; } if (upperColIndexInclusive() < otherMatrix.lowerCol() || otherMatrix.upperColIndexInclusive() < lowerCol()) { return false; } if (upperRowIndexInclusive() < otherMatrix.lowerRow() || otherMatrix.upperRowIndexInclusive() < lowerRow()) { return false; } return true; } @Override public String toString() { return String.format("[%s,%s]-[%s,%s]", lowerCol(), lowerRow(), upperColIndexInclusive(), upperRowIndexInclusive()); } public static enum Mode implements BiFunction<Boolean, Boolean, Boolean> { AND { @Override public Boolean apply(Boolean first, Boolean second) { return first && second; } }, OR { @Override public Boolean apply(Boolean first, Boolean second) { return first || second; } }, NOT_SAME { @Override public Boolean apply(Boolean first, Boolean second) { return !Objects.equals(first, second); } }, SAME { @Override public Boolean apply(Boolean first, Boolean second) { return Objects.equals(first, second); } }; } }