local.laer.app.knapsack.support.OffsetBinaryMatrix.java Source code

Java tutorial

Introduction

Here is the source code for local.laer.app.knapsack.support.OffsetBinaryMatrix.java

Source

/*
 * 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);
            }

        };

    }
}