diff --git a/app/src/test/java/de/uni_marburg/powersort/FinnSort/FasterFinnSortMockitoTest.java b/app/src/test/java/de/uni_marburg/powersort/FinnSort/FasterFinnSortMockitoTest.java new file mode 100644 index 0000000..b52c0ab --- /dev/null +++ b/app/src/test/java/de/uni_marburg/powersort/FinnSort/FasterFinnSortMockitoTest.java @@ -0,0 +1,58 @@ +package de.uni_marburg.powersort.FinnSort; + +import de.uni_marburg.powersort.JUnitUtil; +import de.uni_marburg.powersort.benchmark.NaturalOrder; +import org.junit.jupiter.api.RepeatedTest; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.converter.ConvertWith; +import org.junit.jupiter.params.provider.CsvSource; + +import java.util.Arrays; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +public class FasterFinnSortMockitoTest { + @ParameterizedTest + @CsvSource({ + "4|5|6|1|2|3", + "100|10|11|1|2|3", + "100|11|10|3|2|1", + "24|25|26|27|28|21|22|23|18|19|20|4|5|6|7|8|9|10|11|12|13|14|15|16|17|3|1|2", + }) + void foo(@ConvertWith(JUnitUtil.IntegerArrayConverter.class) Integer[] a) { + printPowers(a); + } + + void printPowers(Object[] a) { + FasterFinnSort ffs = new FasterFinnSort<>(a, NaturalOrder.INSTANCE, null, 0, 0); + FasterFinnSort spiedFfs = spy(ffs); + + // Don't extend short runs with `binarySort()` + when(spiedFfs.minRunLength(anyInt())).thenReturn(0); + + // Capture calculated power values. + final ResultCaptor resultCaptor = new ResultCaptor<>(); + doAnswer(resultCaptor).when(spiedFfs).power(anyInt(), anyInt()); + + + sort(a, spiedFfs); + + + List calculatedPowers = resultCaptor.getAllValues(); + System.out.println(Arrays.toString(calculatedPowers.toArray())); + } + + /** + * Sorts the given array using FasterFinnSort. + */ + void sort(Object[] a, FasterFinnSort ffs) { + // Don't sort short array with `binarySort()` + boolean forcePowersortShortArray = true; + + FasterFinnSortWrapper.sort(a, 0, a.length, NaturalOrder.INSTANCE, null, 0, 0, ffs, forcePowersortShortArray); + } +} diff --git a/app/src/test/java/de/uni_marburg/powersort/FinnSort/FasterFinnSortWrapper.java b/app/src/test/java/de/uni_marburg/powersort/FinnSort/FasterFinnSortWrapper.java new file mode 100644 index 0000000..5be0cf1 --- /dev/null +++ b/app/src/test/java/de/uni_marburg/powersort/FinnSort/FasterFinnSortWrapper.java @@ -0,0 +1,66 @@ +package de.uni_marburg.powersort.FinnSort; + +import java.util.Comparator; + +import static de.uni_marburg.powersort.FinnSort.FasterFinnSort.MIN_MERGE; +import static de.uni_marburg.powersort.FinnSort.FasterFinnSort.binarySort; +import static de.uni_marburg.powersort.FinnSort.FasterFinnSort.countRunAndMakeAscending; + +public class FasterFinnSortWrapper { + /** + * Copy of `FasterFinnSort.java`. Param `ts` has been added to allow dependency injection for testing. + * + * @param ts: If non-null, the creation of `ts` with `new FasterFinnSort<>(a, c, work, workBase, workLen)` in this methods body is skipped. Instead, the given `ts` is used. + */ + public static void sort(T[] a, int lo, int hi, Comparator c, + T[] work, int workBase, int workLen, + FasterFinnSort ts, boolean forcePowersortShortArray) { + assert c != null && a != null && lo >= 0 && lo <= hi && hi <= a.length; + + int nRemaining = hi - lo; + if (nRemaining < 2) + return; // Arrays of size 0 and 1 are always sorted + + // If array is small, do a "mini-TimSort" with no merges + if (!forcePowersortShortArray && nRemaining < MIN_MERGE) { + int initRunLen = countRunAndMakeAscending(a, lo, hi, c); + binarySort(a, lo, hi, lo + initRunLen, c); + return; + } + + /* + * March over the array once, left to right, finding natural runs, + * extending short natural runs to minRun elements, and merging runs + * to maintain stack invariant. + */ + if (ts == null) ts = new FasterFinnSort<>(a, c, work, workBase, workLen); + int minRun = ts.minRunLength(nRemaining); + System.out.println("minRun: " + minRun); + do { + // Identify next run + int runLen = countRunAndMakeAscending(a, lo, hi, c); + System.out.println("runLen: " + runLen); + + // If run is short, extend to min(minRun, nRemaining) + if (runLen < minRun) { + System.out.println("Extend run of length " + runLen + " to " + Math.min(minRun, nRemaining)); + int force = nRemaining <= minRun ? nRemaining : minRun; + binarySort(a, lo, lo + force, lo + runLen, c); + runLen = force; + } + + // Push run onto pending-run stack, and maybe merge + ts.pushRun(lo, runLen, hi - lo); + ts.mergeCollapse(); + + // Advance to find next run + lo += runLen; + nRemaining -= runLen; + } while (nRemaining != 0); + + // Merge all remaining runs to complete sort + assert lo == hi; + ts.mergeForceCollapse(); + assert ts.stackSize == 1; + } +} diff --git a/app/src/test/java/de/uni_marburg/powersort/FinnSort/ResultCaptor.java b/app/src/test/java/de/uni_marburg/powersort/FinnSort/ResultCaptor.java new file mode 100644 index 0000000..05ca6d7 --- /dev/null +++ b/app/src/test/java/de/uni_marburg/powersort/FinnSort/ResultCaptor.java @@ -0,0 +1,31 @@ +package de.uni_marburg.powersort.FinnSort; + +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import java.util.ArrayList; +import java.util.List; + + +/** + * Source: https://stackoverflow.com/a/25694142/6334421 + *

+ * Alternative which didn't work: https://github.com/mockito/mockito/issues/2422 + */ +public class ResultCaptor implements Answer { + private final List results = new ArrayList<>(); + + /** + * Returns all captured values. + */ + public List getAllValues() { + return results; + } + + @Override + public T answer(InvocationOnMock invocationOnMock) throws Throwable { + T result = (T) invocationOnMock.callRealMethod(); + results.add(result); + return result; + } +}