<commit_before>package com.fivehundredpx.greedolayout;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by <NAME> on 15-08-24.
 */

public class GreedoLayoutSizeCalculator {
    public interface SizeCalculatorDelegate {
        double aspectRatioForIndex(int index);
    }

    private static final int DEFAULT_MAX_ROW_HEIGHT = 600;
    private static int mMaxRowHeight = DEFAULT_MAX_ROW_HEIGHT;

    private static final int INVALID_CONTENT_WIDTH = -1;
    private int mContentWidth = INVALID_CONTENT_WIDTH;

    private boolean mFixedHeight = false;

    private SizeCalculatorDelegate mSizeCalculatorDelegate;

    private List<Size> mSizeForChildAtPosition;
    private List<Integer> mFirstChildPositionForRow;
    private List<Integer> mRowForChildPosition;

    public GreedoLayoutSizeCalculator(SizeCalculatorDelegate sizeCalculatorDelegate) {
        mSizeCalculatorDelegate = sizeCalculatorDelegate;

        mSizeForChildAtPosition = new ArrayList<>();
        mFirstChildPositionForRow = new ArrayList<>();
        mRowForChildPosition = new ArrayList<>();
    }

    public void setContentWidth(int contentWidth) {
        if (mContentWidth != contentWidth) {
            mContentWidth = contentWidth;
            reset();
        }
    }

    public void setMaxRowHeight(int maxRowHeight) {
        if (mMaxRowHeight != maxRowHeight) {
            mMaxRowHeight = maxRowHeight;
            reset();
        }
    }

    public void setFixedHeight(boolean fixedHeight) {
        if (mFixedHeight != fixedHeight) {
            mFixedHeight = fixedHeight;
            reset();
        }
    }

    Size sizeForChildAtPosition(int position) {
        if (position >= mSizeForChildAtPosition.size()) {
            computeChildSizesUpToPosition(position);
        }

        return mSizeForChildAtPosition.get(position);
    }

    int getFirstChildPositionForRow(int row) {
        if (row >= mFirstChildPositionForRow.size()) {
            computeFirstChildPositionsUpToRow(row);
        }
        return mFirstChildPositionForRow.get(row);
    }

    private void computeFirstChildPositionsUpToRow(int row) {
        // TODO: Rewrite this? Looks dangerous but in reality should be fine. I'd like something
        // less alarming though.
        while (row >= mFirstChildPositionForRow.size()) {
            computeChildSizesUpToPosition(mSizeForChildAtPosition.size() + 1);
        }
    }

    int getRowForChildPosition(int position) {
        if (position >= mRowForChildPosition.size()) {
            computeChildSizesUpToPosition(position);
        }

        return mRowForChildPosition.get(position);
    }

    void reset() {
        mSizeForChildAtPosition.clear();
        mFirstChildPositionForRow.clear();
        mRowForChildPosition.clear();
    }

    public void computeChildSizesUpToPosition(int lastPosition) {
        if (mContentWidth == INVALID_CONTENT_WIDTH) {
            throw new RuntimeException("Invalid content width. Did you forget to set it?");
        }

        if (mSizeCalculatorDelegate == null) {
            throw new RuntimeException("Size calculator delegate is missing. Did you forget to set it?");
        }

        int firstUncomputedChildPosition = mSizeForChildAtPosition.size();
        int row = mRowForChildPosition.size() > 0
                ? mRowForChildPosition.get(mRowForChildPosition.size() - 1) + 1 : 0;

        double rowAspectRatio = 0.0;
        int currentRowHeight = Integer.MAX_VALUE;
        List<Double> aspectRatiosForRow = new ArrayList<>();
        for (int pos = firstUncomputedChildPosition; pos < lastPosition || currentRowHeight > mMaxRowHeight; pos++) {
            double posAspectRatio = mSizeCalculatorDelegate.aspectRatioForIndex(pos);
            rowAspectRatio += posAspectRatio;
            aspectRatiosForRow.add(posAspectRatio);

            currentRowHeight = (int)Math.ceil(mContentWidth / rowAspectRatio);
            if (currentRowHeight <= mMaxRowHeight) { // Row is full
                int rowChildCount = aspectRatiosForRow.size();
                mFirstChildPositionForRow.add(pos - rowChildCount + 1);

                int availableSpace = mContentWidth;
                for (Double aspectRatio : aspectRatiosForRow) {
                    int scaledPhotoWidth = (int)Math.ceil(currentRowHeight * aspectRatio);
                    scaledPhotoWidth = Math.min(availableSpace, scaledPhotoWidth);

                    mSizeForChildAtPosition.add(new Size(scaledPhotoWidth, currentRowHeight));
                    mRowForChildPosition.add(row);

                    availableSpace = availableSpace - scaledPhotoWidth;
                }

                aspectRatiosForRow.clear();
                rowAspectRatio = 0.0;
                row++;
            }
        }
    }
}<commit_msg>Update size calculator to support fixed height rows<commit_after>package com.fivehundredpx.greedolayout;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by <NAME> on 15-08-24.
 */

public class GreedoLayoutSizeCalculator {
    public interface SizeCalculatorDelegate {
        double aspectRatioForIndex(int index);
    }

    private static final int DEFAULT_MAX_ROW_HEIGHT = 600;
    private static int mMaxRowHeight = DEFAULT_MAX_ROW_HEIGHT;

    private static final int INVALID_CONTENT_WIDTH = -1;
    private int mContentWidth = INVALID_CONTENT_WIDTH;

    // When in fixed height mode and the item's width is less than this percentage, don't try to
    // fit the item, overflow it to the next row and grow the existing items.
    private static final double VALID_ITEM_SLACK_THRESHOLD = 2.0 / 3.0;

    private boolean mIsFixedHeight = false;

    private SizeCalculatorDelegate mSizeCalculatorDelegate;

    private List<Size> mSizeForChildAtPosition;
    private List<Integer> mFirstChildPositionForRow;
    private List<Integer> mRowForChildPosition;

    public GreedoLayoutSizeCalculator(SizeCalculatorDelegate sizeCalculatorDelegate) {
        mSizeCalculatorDelegate = sizeCalculatorDelegate;

        mSizeForChildAtPosition = new ArrayList<>();
        mFirstChildPositionForRow = new ArrayList<>();
        mRowForChildPosition = new ArrayList<>();
    }

    public void setContentWidth(int contentWidth) {
        if (mContentWidth != contentWidth) {
            mContentWidth = contentWidth;
            reset();
        }
    }

    public void setMaxRowHeight(int maxRowHeight) {
        if (mMaxRowHeight != maxRowHeight) {
            mMaxRowHeight = maxRowHeight;
            reset();
        }
    }

    public void setFixedHeight(boolean fixedHeight) {
        if (mIsFixedHeight != fixedHeight) {
            mIsFixedHeight = fixedHeight;
            reset();
        }
    }

    Size sizeForChildAtPosition(int position) {
        if (position >= mSizeForChildAtPosition.size()) {
            computeChildSizesUpToPosition(position);
        }

        return mSizeForChildAtPosition.get(position);
    }

    int getFirstChildPositionForRow(int row) {
        if (row >= mFirstChildPositionForRow.size()) {
            computeFirstChildPositionsUpToRow(row);
        }
        return mFirstChildPositionForRow.get(row);
    }

    private void computeFirstChildPositionsUpToRow(int row) {
        // TODO: Rewrite this? Looks dangerous but in reality should be fine. I'd like something
        //       less alarming though.
        while (row >= mFirstChildPositionForRow.size()) {
            computeChildSizesUpToPosition(mSizeForChildAtPosition.size() + 1);
        }
    }

    int getRowForChildPosition(int position) {
        if (position >= mRowForChildPosition.size()) {
            computeChildSizesUpToPosition(position);
        }

        return mRowForChildPosition.get(position);
    }

    void reset() {
        mSizeForChildAtPosition.clear();
        mFirstChildPositionForRow.clear();
        mRowForChildPosition.clear();
    }

    public void computeChildSizesUpToPosition(int lastPosition) {
        if (mContentWidth == INVALID_CONTENT_WIDTH) {
            throw new RuntimeException("Invalid content width. Did you forget to set it?");
        }

        if (mSizeCalculatorDelegate == null) {
            throw new RuntimeException("Size calculator delegate is missing. Did you forget to set it?");
        }

        int firstUncomputedChildPosition = mSizeForChildAtPosition.size();
        int row = mRowForChildPosition.size() > 0
                ? mRowForChildPosition.get(mRowForChildPosition.size() - 1) + 1 : 0;

        double currentRowAspectRatio = 0.0;
        List<Double> itemAspectRatios = new ArrayList<>();
        int currentRowHeight = mIsFixedHeight ? mMaxRowHeight : Integer.MAX_VALUE;

        int currentRowWidth = 0;
        int pos = firstUncomputedChildPosition;
        while (pos < lastPosition || (mIsFixedHeight ? currentRowWidth <= mContentWidth : currentRowHeight > mMaxRowHeight)) {
            double posAspectRatio = mSizeCalculatorDelegate.aspectRatioForIndex(pos);
            currentRowAspectRatio += posAspectRatio;
            itemAspectRatios.add(posAspectRatio);

            currentRowWidth = calculateWidth(currentRowHeight, currentRowAspectRatio);
            if (!mIsFixedHeight) {
                currentRowHeight = calculateHeight(mContentWidth, currentRowAspectRatio);
            }

            boolean isRowFull = mIsFixedHeight ? currentRowWidth > mContentWidth : currentRowHeight <= mMaxRowHeight;
            if (isRowFull) {
                int rowChildCount = itemAspectRatios.size();
                mFirstChildPositionForRow.add(pos - rowChildCount + 1);

                int[] itemSlacks = new int[rowChildCount];
                if (mIsFixedHeight) {
                    itemSlacks = distributeRowSlack(currentRowWidth, rowChildCount, itemAspectRatios);

                    if (!hasValidItemSlacks(itemSlacks, itemAspectRatios)) {
                        int lastItemWidth = calculateWidth(currentRowHeight,
                                itemAspectRatios.get(itemAspectRatios.size() - 1));
                        currentRowWidth -= lastItemWidth;
                        rowChildCount -= 1;
                        itemAspectRatios.remove(itemAspectRatios.size() - 1);

                        itemSlacks = distributeRowSlack(currentRowWidth, rowChildCount, itemAspectRatios);
                    }
                }

                int availableSpace = mContentWidth;
                for (int i = 0; i < rowChildCount; i++) {
                    int itemWidth = calculateWidth(currentRowHeight, itemAspectRatios.get(i)) - itemSlacks[i];
                    itemWidth = Math.min(availableSpace, itemWidth);

                    mSizeForChildAtPosition.add(new Size(itemWidth, currentRowHeight));
                    mRowForChildPosition.add(row);

                    availableSpace -= itemWidth;
                }

                itemAspectRatios.clear();
                currentRowAspectRatio = 0.0;
                row++;
            }

            pos++;
        }
    }

    private int[] distributeRowSlack(int rowWidth, int rowChildCount, List<Double> itemAspectRatios) {
        return distributeRowSlack(rowWidth - mContentWidth, rowWidth, rowChildCount, itemAspectRatios);
    }

    private int[] distributeRowSlack(int rowSlack, int rowWidth, int rowChildCount, List<Double> itemAspectRatios) {
        int itemSlacks[] = new int[rowChildCount];

        for (int i = 0; i < rowChildCount; i++) {
            double itemWidth = mMaxRowHeight * itemAspectRatios.get(i);
            itemSlacks[i] = (int) (rowSlack * (itemWidth / rowWidth));
        }

        return itemSlacks;
    }

    private boolean hasValidItemSlacks(int[] itemSlacks, List<Double> itemAspectRatios) {
        for (int i = 0; i < itemSlacks.length; i++) {
            int itemWidth = (int) (itemAspectRatios.get(i) * mMaxRowHeight);
            if (!isValidItemSlack(itemSlacks[i], itemWidth)) {
                return false;
            }
        }

        return true;
    }

    private boolean isValidItemSlack(int itemSlack, int itemWidth) {
        return (itemWidth - itemSlack) / (double) itemWidth > VALID_ITEM_SLACK_THRESHOLD;
    }

    private int calculateWidth(int itemHeight, double aspectRatio) {
        return (int) Math.ceil(itemHeight * aspectRatio);
    }

    private int calculateHeight(int itemWidth, double aspectRatio) {
        return (int) Math.ceil(itemWidth / aspectRatio);
    }
}
{'fraction_non_alphanumeric': 0.08146541335261509, 'fraction_numerical': 0.0040973728609303445, 'mean_word_length': 2.4946659180235824, 'pattern_counts': {'":': 0, '<': 32, '<?xml version=': 0, '>': 38, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1798070', 'n_tokens_mistral': 3466, 'n_tokens_neox': 3261, 'n_words': 733}