From 6e9057d0f28799b7483d96097fc7ea2247ebf8d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89amonn=20McManus?= Date: Wed, 4 May 2022 05:16:21 -0700 Subject: [PATCH] Add `rotate()` methods for other primitive array types, except `boolean`. Also add an explanation of possible alternative algorithms, in `Ints.rotate`. RELNOTES=`primitives`: Added `rotate()` for arrays of all primitive types. PiperOrigin-RevId: 446428068 --- .../common/cache/LocalLoadingCacheTest.java | 2 +- .../common/primitives/BooleansTest.java | 231 ++++++++++++++++++ .../google/common/primitives/BytesTest.java | 97 ++++++++ .../google/common/primitives/CharsTest.java | 197 +++++++++++++++ .../google/common/primitives/DoublesTest.java | 97 ++++++++ .../google/common/primitives/FloatsTest.java | 97 ++++++++ .../google/common/primitives/LongsTest.java | 97 ++++++++ .../google/common/primitives/ShortsTest.java | 97 ++++++++ .../google/common/primitives/Booleans.java | 50 ++++ .../com/google/common/primitives/Bytes.java | 50 ++++ .../com/google/common/primitives/Chars.java | 50 ++++ .../com/google/common/primitives/Doubles.java | 50 ++++ .../com/google/common/primitives/Floats.java | 50 ++++ .../com/google/common/primitives/Ints.java | 27 ++ .../com/google/common/primitives/Longs.java | 50 ++++ .../com/google/common/primitives/Shorts.java | 50 ++++ .../common/cache/LocalLoadingCacheTest.java | 2 +- .../common/primitives/BooleansTest.java | 231 ++++++++++++++++++ .../google/common/primitives/BytesTest.java | 97 ++++++++ .../google/common/primitives/CharsTest.java | 197 +++++++++++++++ .../google/common/primitives/DoublesTest.java | 97 ++++++++ .../google/common/primitives/FloatsTest.java | 97 ++++++++ .../google/common/primitives/LongsTest.java | 97 ++++++++ .../google/common/primitives/ShortsTest.java | 97 ++++++++ .../google/common/primitives/Booleans.java | 50 ++++ .../com/google/common/primitives/Bytes.java | 50 ++++ .../com/google/common/primitives/Chars.java | 50 ++++ .../com/google/common/primitives/Doubles.java | 50 ++++ .../com/google/common/primitives/Floats.java | 50 ++++ .../com/google/common/primitives/Ints.java | 27 ++ .../com/google/common/primitives/Longs.java | 50 ++++ .../com/google/common/primitives/Shorts.java | 50 ++++ 32 files changed, 2582 insertions(+), 2 deletions(-) diff --git a/android/guava-tests/test/com/google/common/cache/LocalLoadingCacheTest.java b/android/guava-tests/test/com/google/common/cache/LocalLoadingCacheTest.java index 06c007a9266e..6b73bdc422fc 100644 --- a/android/guava-tests/test/com/google/common/cache/LocalLoadingCacheTest.java +++ b/android/guava-tests/test/com/google/common/cache/LocalLoadingCacheTest.java @@ -157,7 +157,7 @@ public void testStatsNoops() { assertThat(map).containsEntry(three, one); assertThat(map).containsEntry(one, two); - // TODO(user): Confirm with fry@ that this is a reasonable substitute. + // TODO(cgruber): Confirm with fry@ that this is a reasonable substitute. // Set> entries = map.entrySet(); // assertThat(entries).containsExactly( // Maps.immutableEntry(three, one), Maps.immutableEntry(one, two)); diff --git a/android/guava-tests/test/com/google/common/primitives/BooleansTest.java b/android/guava-tests/test/com/google/common/primitives/BooleansTest.java index e91b19631754..c4367fa5168a 100644 --- a/android/guava-tests/test/com/google/common/primitives/BooleansTest.java +++ b/android/guava-tests/test/com/google/common/primitives/BooleansTest.java @@ -209,6 +209,237 @@ public void testReverseIndexed() { new boolean[] {true, true, false, false}, 1, 3, new boolean[] {true, false, true, false}); } + private static void testRotate(boolean[] input, int distance, boolean[] expectedOutput) { + input = Arrays.copyOf(input, input.length); + Booleans.rotate(input, distance); + assertThat(input).isEqualTo(expectedOutput); + } + + private static void testRotate( + boolean[] input, int distance, int fromIndex, int toIndex, boolean[] expectedOutput) { + input = Arrays.copyOf(input, input.length); + Booleans.rotate(input, distance, fromIndex, toIndex); + assertThat(input).isEqualTo(expectedOutput); + } + + public void testRotate() { + testRotate(new boolean[] {}, -1, new boolean[] {}); + testRotate(new boolean[] {}, 0, new boolean[] {}); + testRotate(new boolean[] {}, 1, new boolean[] {}); + + testRotate(new boolean[] {true}, -2, new boolean[] {true}); + testRotate(new boolean[] {true}, -1, new boolean[] {true}); + testRotate(new boolean[] {true}, 0, new boolean[] {true}); + testRotate(new boolean[] {true}, 1, new boolean[] {true}); + testRotate(new boolean[] {true}, 2, new boolean[] {true}); + + testRotate(new boolean[] {true, false}, -3, new boolean[] {false, true}); + testRotate(new boolean[] {true, false}, -1, new boolean[] {false, true}); + testRotate(new boolean[] {true, false}, -2, new boolean[] {true, false}); + testRotate(new boolean[] {true, false}, 0, new boolean[] {true, false}); + testRotate(new boolean[] {true, false}, 1, new boolean[] {false, true}); + testRotate(new boolean[] {true, false}, 2, new boolean[] {true, false}); + testRotate(new boolean[] {true, false}, 3, new boolean[] {false, true}); + + testRotate(new boolean[] {true, false, true}, -5, new boolean[] {true, true, false}); + testRotate(new boolean[] {true, false, true}, -4, new boolean[] {false, true, true}); + testRotate(new boolean[] {true, false, true}, -3, new boolean[] {true, false, true}); + testRotate(new boolean[] {true, false, true}, -2, new boolean[] {true, true, false}); + testRotate(new boolean[] {true, false, true}, -1, new boolean[] {false, true, true}); + testRotate(new boolean[] {true, false, true}, 0, new boolean[] {true, false, true}); + testRotate(new boolean[] {true, false, true}, 1, new boolean[] {true, true, false}); + testRotate(new boolean[] {true, false, true}, 2, new boolean[] {false, true, true}); + testRotate(new boolean[] {true, false, true}, 3, new boolean[] {true, false, true}); + testRotate(new boolean[] {true, false, true}, 4, new boolean[] {true, true, false}); + testRotate(new boolean[] {true, false, true}, 5, new boolean[] {false, true, true}); + + testRotate( + new boolean[] {true, false, true, false}, -9, new boolean[] {false, true, false, true}); + testRotate( + new boolean[] {true, false, true, false}, -5, new boolean[] {false, true, false, true}); + testRotate( + new boolean[] {true, false, true, false}, -1, new boolean[] {false, true, false, true}); + testRotate( + new boolean[] {true, false, true, false}, 0, new boolean[] {true, false, true, false}); + testRotate( + new boolean[] {true, false, true, false}, 1, new boolean[] {false, true, false, true}); + testRotate( + new boolean[] {true, false, true, false}, 5, new boolean[] {false, true, false, true}); + testRotate( + new boolean[] {true, false, true, false}, 9, new boolean[] {false, true, false, true}); + + testRotate( + new boolean[] {true, false, true, false, true}, + -6, + new boolean[] {false, true, false, true, true}); + testRotate( + new boolean[] {true, false, true, false, true}, + -4, + new boolean[] {true, true, false, true, false}); + testRotate( + new boolean[] {true, false, true, false, true}, + -3, + new boolean[] {false, true, true, false, true}); + testRotate( + new boolean[] {true, false, true, false, true}, + -1, + new boolean[] {false, true, false, true, true}); + testRotate( + new boolean[] {true, false, true, false, true}, + 0, + new boolean[] {true, false, true, false, true}); + testRotate( + new boolean[] {true, false, true, false, true}, + 1, + new boolean[] {true, true, false, true, false}); + testRotate( + new boolean[] {true, false, true, false, true}, + 3, + new boolean[] {true, false, true, true, false}); + testRotate( + new boolean[] {true, false, true, false, true}, + 4, + new boolean[] {false, true, false, true, true}); + testRotate( + new boolean[] {true, false, true, false, true}, + 6, + new boolean[] {true, true, false, true, false}); + } + + public void testRotateIndexed() { + testRotate(new boolean[] {}, 0, 0, 0, new boolean[] {}); + + testRotate(new boolean[] {true}, 0, 0, 1, new boolean[] {true}); + testRotate(new boolean[] {true}, 1, 0, 1, new boolean[] {true}); + testRotate(new boolean[] {true}, 1, 1, 1, new boolean[] {true}); + + // Rotate the central 5 elements, leaving the ends as-is + testRotate( + new boolean[] {false, true, false, true, false, true, false}, + -6, + 1, + 6, + new boolean[] {false, false, true, false, true, true, false}); + testRotate( + new boolean[] {false, true, false, true, false, true, false}, + -1, + 1, + 6, + new boolean[] {false, false, true, false, true, true, false}); + testRotate( + new boolean[] {false, true, false, true, false, true, false}, + 0, + 1, + 6, + new boolean[] {false, true, false, true, false, true, false}); + testRotate( + new boolean[] {false, true, false, true, false, true, false}, + 5, + 1, + 6, + new boolean[] {false, true, false, true, false, true, false}); + testRotate( + new boolean[] {false, true, false, true, false, true, false}, + 14, + 1, + 6, + new boolean[] {false, false, true, false, true, true, false}); + + // Rotate the first three elements + testRotate( + new boolean[] {false, true, false, true, false, true, false}, + -2, + 0, + 3, + new boolean[] {false, false, true, true, false, true, false}); + testRotate( + new boolean[] {false, true, false, true, false, true, false}, + -1, + 0, + 3, + new boolean[] {true, false, false, true, false, true, false}); + testRotate( + new boolean[] {false, true, false, true, false, true, false}, + 0, + 0, + 3, + new boolean[] {false, true, false, true, false, true, false}); + testRotate( + new boolean[] {false, true, false, true, false, true, false}, + 1, + 0, + 3, + new boolean[] {false, false, true, true, false, true, false}); + testRotate( + new boolean[] {false, true, false, true, false, true, false}, + 2, + 0, + 3, + new boolean[] {true, false, false, true, false, true, false}); + + // Rotate the last four elements + testRotate( + new boolean[] {false, true, false, true, false, true, false}, + -6, + 3, + 7, + new boolean[] {false, true, false, true, false, true, false}); + testRotate( + new boolean[] {false, true, false, true, false, true, false}, + -5, + 3, + 7, + new boolean[] {false, true, false, false, true, false, true}); + testRotate( + new boolean[] {false, true, false, true, false, true, false}, + -4, + 3, + 7, + new boolean[] {false, true, false, true, false, true, false}); + testRotate( + new boolean[] {false, true, false, true, false, true, false}, + -3, + 3, + 7, + new boolean[] {false, true, false, false, true, false, true}); + testRotate( + new boolean[] {false, true, false, true, false, true, false}, + -2, + 3, + 7, + new boolean[] {false, true, false, true, false, true, false}); + testRotate( + new boolean[] {false, true, false, true, false, true, false}, + -1, + 3, + 7, + new boolean[] {false, true, false, false, true, false, true}); + testRotate( + new boolean[] {false, true, false, true, false, true, false}, + 0, + 3, + 7, + new boolean[] {false, true, false, true, false, true, false}); + testRotate( + new boolean[] {false, true, false, true, false, true, false}, + 1, + 3, + 7, + new boolean[] {false, true, false, false, true, false, true}); + testRotate( + new boolean[] {false, true, false, true, false, true, false}, + 2, + 3, + 7, + new boolean[] {false, true, false, true, false, true, false}); + testRotate( + new boolean[] {false, true, false, true, false, true, false}, + 3, + 3, + 7, + new boolean[] {false, true, false, false, true, false, true}); + } + public void testToArray() { // need explicit type parameter to avoid javac warning!? List none = Arrays.asList(); diff --git a/android/guava-tests/test/com/google/common/primitives/BytesTest.java b/android/guava-tests/test/com/google/common/primitives/BytesTest.java index a1716bcc793d..da06a34bb95d 100644 --- a/android/guava-tests/test/com/google/common/primitives/BytesTest.java +++ b/android/guava-tests/test/com/google/common/primitives/BytesTest.java @@ -270,6 +270,103 @@ public void testReverseIndexed() { testReverse(new byte[] {-1, 1, -2, 2}, 1, 3, new byte[] {-1, -2, 1, 2}); } + private static void testRotate(byte[] input, int distance, byte[] expectedOutput) { + input = Arrays.copyOf(input, input.length); + Bytes.rotate(input, distance); + assertThat(input).isEqualTo(expectedOutput); + } + + private static void testRotate( + byte[] input, int distance, int fromIndex, int toIndex, byte[] expectedOutput) { + input = Arrays.copyOf(input, input.length); + Bytes.rotate(input, distance, fromIndex, toIndex); + assertThat(input).isEqualTo(expectedOutput); + } + + public void testRotate() { + testRotate(new byte[] {}, -1, new byte[] {}); + testRotate(new byte[] {}, 0, new byte[] {}); + testRotate(new byte[] {}, 1, new byte[] {}); + + testRotate(new byte[] {1}, -2, new byte[] {1}); + testRotate(new byte[] {1}, -1, new byte[] {1}); + testRotate(new byte[] {1}, 0, new byte[] {1}); + testRotate(new byte[] {1}, 1, new byte[] {1}); + testRotate(new byte[] {1}, 2, new byte[] {1}); + + testRotate(new byte[] {1, 2}, -3, new byte[] {2, 1}); + testRotate(new byte[] {1, 2}, -1, new byte[] {2, 1}); + testRotate(new byte[] {1, 2}, -2, new byte[] {1, 2}); + testRotate(new byte[] {1, 2}, 0, new byte[] {1, 2}); + testRotate(new byte[] {1, 2}, 1, new byte[] {2, 1}); + testRotate(new byte[] {1, 2}, 2, new byte[] {1, 2}); + testRotate(new byte[] {1, 2}, 3, new byte[] {2, 1}); + + testRotate(new byte[] {1, 2, 3}, -5, new byte[] {3, 1, 2}); + testRotate(new byte[] {1, 2, 3}, -4, new byte[] {2, 3, 1}); + testRotate(new byte[] {1, 2, 3}, -3, new byte[] {1, 2, 3}); + testRotate(new byte[] {1, 2, 3}, -2, new byte[] {3, 1, 2}); + testRotate(new byte[] {1, 2, 3}, -1, new byte[] {2, 3, 1}); + testRotate(new byte[] {1, 2, 3}, 0, new byte[] {1, 2, 3}); + testRotate(new byte[] {1, 2, 3}, 1, new byte[] {3, 1, 2}); + testRotate(new byte[] {1, 2, 3}, 2, new byte[] {2, 3, 1}); + testRotate(new byte[] {1, 2, 3}, 3, new byte[] {1, 2, 3}); + testRotate(new byte[] {1, 2, 3}, 4, new byte[] {3, 1, 2}); + testRotate(new byte[] {1, 2, 3}, 5, new byte[] {2, 3, 1}); + + testRotate(new byte[] {1, 2, 3, 4}, -9, new byte[] {2, 3, 4, 1}); + testRotate(new byte[] {1, 2, 3, 4}, -5, new byte[] {2, 3, 4, 1}); + testRotate(new byte[] {1, 2, 3, 4}, -1, new byte[] {2, 3, 4, 1}); + testRotate(new byte[] {1, 2, 3, 4}, 0, new byte[] {1, 2, 3, 4}); + testRotate(new byte[] {1, 2, 3, 4}, 1, new byte[] {4, 1, 2, 3}); + testRotate(new byte[] {1, 2, 3, 4}, 5, new byte[] {4, 1, 2, 3}); + testRotate(new byte[] {1, 2, 3, 4}, 9, new byte[] {4, 1, 2, 3}); + + testRotate(new byte[] {1, 2, 3, 4, 5}, -6, new byte[] {2, 3, 4, 5, 1}); + testRotate(new byte[] {1, 2, 3, 4, 5}, -4, new byte[] {5, 1, 2, 3, 4}); + testRotate(new byte[] {1, 2, 3, 4, 5}, -3, new byte[] {4, 5, 1, 2, 3}); + testRotate(new byte[] {1, 2, 3, 4, 5}, -1, new byte[] {2, 3, 4, 5, 1}); + testRotate(new byte[] {1, 2, 3, 4, 5}, 0, new byte[] {1, 2, 3, 4, 5}); + testRotate(new byte[] {1, 2, 3, 4, 5}, 1, new byte[] {5, 1, 2, 3, 4}); + testRotate(new byte[] {1, 2, 3, 4, 5}, 3, new byte[] {3, 4, 5, 1, 2}); + testRotate(new byte[] {1, 2, 3, 4, 5}, 4, new byte[] {2, 3, 4, 5, 1}); + testRotate(new byte[] {1, 2, 3, 4, 5}, 6, new byte[] {5, 1, 2, 3, 4}); + } + + public void testRotateIndexed() { + testRotate(new byte[] {}, 0, 0, 0, new byte[] {}); + + testRotate(new byte[] {1}, 0, 0, 1, new byte[] {1}); + testRotate(new byte[] {1}, 1, 0, 1, new byte[] {1}); + testRotate(new byte[] {1}, 1, 1, 1, new byte[] {1}); + + // Rotate the central 5 elements, leaving the ends as-is + testRotate(new byte[] {0, 1, 2, 3, 4, 5, 6}, -6, 1, 6, new byte[] {0, 2, 3, 4, 5, 1, 6}); + testRotate(new byte[] {0, 1, 2, 3, 4, 5, 6}, -1, 1, 6, new byte[] {0, 2, 3, 4, 5, 1, 6}); + testRotate(new byte[] {0, 1, 2, 3, 4, 5, 6}, 0, 1, 6, new byte[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new byte[] {0, 1, 2, 3, 4, 5, 6}, 5, 1, 6, new byte[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new byte[] {0, 1, 2, 3, 4, 5, 6}, 14, 1, 6, new byte[] {0, 2, 3, 4, 5, 1, 6}); + + // Rotate the first three elements + testRotate(new byte[] {0, 1, 2, 3, 4, 5, 6}, -2, 0, 3, new byte[] {2, 0, 1, 3, 4, 5, 6}); + testRotate(new byte[] {0, 1, 2, 3, 4, 5, 6}, -1, 0, 3, new byte[] {1, 2, 0, 3, 4, 5, 6}); + testRotate(new byte[] {0, 1, 2, 3, 4, 5, 6}, 0, 0, 3, new byte[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new byte[] {0, 1, 2, 3, 4, 5, 6}, 1, 0, 3, new byte[] {2, 0, 1, 3, 4, 5, 6}); + testRotate(new byte[] {0, 1, 2, 3, 4, 5, 6}, 2, 0, 3, new byte[] {1, 2, 0, 3, 4, 5, 6}); + + // Rotate the last four elements + testRotate(new byte[] {0, 1, 2, 3, 4, 5, 6}, -6, 3, 7, new byte[] {0, 1, 2, 5, 6, 3, 4}); + testRotate(new byte[] {0, 1, 2, 3, 4, 5, 6}, -5, 3, 7, new byte[] {0, 1, 2, 4, 5, 6, 3}); + testRotate(new byte[] {0, 1, 2, 3, 4, 5, 6}, -4, 3, 7, new byte[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new byte[] {0, 1, 2, 3, 4, 5, 6}, -3, 3, 7, new byte[] {0, 1, 2, 6, 3, 4, 5}); + testRotate(new byte[] {0, 1, 2, 3, 4, 5, 6}, -2, 3, 7, new byte[] {0, 1, 2, 5, 6, 3, 4}); + testRotate(new byte[] {0, 1, 2, 3, 4, 5, 6}, -1, 3, 7, new byte[] {0, 1, 2, 4, 5, 6, 3}); + testRotate(new byte[] {0, 1, 2, 3, 4, 5, 6}, 0, 3, 7, new byte[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new byte[] {0, 1, 2, 3, 4, 5, 6}, 1, 3, 7, new byte[] {0, 1, 2, 6, 3, 4, 5}); + testRotate(new byte[] {0, 1, 2, 3, 4, 5, 6}, 2, 3, 7, new byte[] {0, 1, 2, 5, 6, 3, 4}); + testRotate(new byte[] {0, 1, 2, 3, 4, 5, 6}, 3, 3, 7, new byte[] {0, 1, 2, 4, 5, 6, 3}); + } + @GwtIncompatible // NullPointerTester public void testNulls() { new NullPointerTester().testAllPublicStaticMethods(Bytes.class); diff --git a/android/guava-tests/test/com/google/common/primitives/CharsTest.java b/android/guava-tests/test/com/google/common/primitives/CharsTest.java index 099e02443b15..3f1d4156e169 100644 --- a/android/guava-tests/test/com/google/common/primitives/CharsTest.java +++ b/android/guava-tests/test/com/google/common/primitives/CharsTest.java @@ -359,6 +359,203 @@ public void testReverseIndexed() { testReverse(new char[] {'A', '1', 'B', '2'}, 1, 3, new char[] {'A', 'B', '1', '2'}); } + private static void testRotate(char[] input, int distance, char[] expectedOutput) { + input = Arrays.copyOf(input, input.length); + Chars.rotate(input, distance); + assertThat(input).isEqualTo(expectedOutput); + } + + private static void testRotate( + char[] input, int distance, int fromIndex, int toIndex, char[] expectedOutput) { + input = Arrays.copyOf(input, input.length); + Chars.rotate(input, distance, fromIndex, toIndex); + assertThat(input).isEqualTo(expectedOutput); + } + + public void testRotate() { + testRotate(new char[] {}, -1, new char[] {}); + testRotate(new char[] {}, 0, new char[] {}); + testRotate(new char[] {}, 1, new char[] {}); + + testRotate(new char[] {'1'}, -2, new char[] {'1'}); + testRotate(new char[] {'1'}, -1, new char[] {'1'}); + testRotate(new char[] {'1'}, 0, new char[] {'1'}); + testRotate(new char[] {'1'}, 1, new char[] {'1'}); + testRotate(new char[] {'1'}, 2, new char[] {'1'}); + + testRotate(new char[] {'1', '2'}, -3, new char[] {'2', '1'}); + testRotate(new char[] {'1', '2'}, -1, new char[] {'2', '1'}); + testRotate(new char[] {'1', '2'}, -2, new char[] {'1', '2'}); + testRotate(new char[] {'1', '2'}, 0, new char[] {'1', '2'}); + testRotate(new char[] {'1', '2'}, 1, new char[] {'2', '1'}); + testRotate(new char[] {'1', '2'}, 2, new char[] {'1', '2'}); + testRotate(new char[] {'1', '2'}, 3, new char[] {'2', '1'}); + + testRotate(new char[] {'1', '2', '3'}, -5, new char[] {'3', '1', '2'}); + testRotate(new char[] {'1', '2', '3'}, -4, new char[] {'2', '3', '1'}); + testRotate(new char[] {'1', '2', '3'}, -3, new char[] {'1', '2', '3'}); + testRotate(new char[] {'1', '2', '3'}, -2, new char[] {'3', '1', '2'}); + testRotate(new char[] {'1', '2', '3'}, -1, new char[] {'2', '3', '1'}); + testRotate(new char[] {'1', '2', '3'}, 0, new char[] {'1', '2', '3'}); + testRotate(new char[] {'1', '2', '3'}, 1, new char[] {'3', '1', '2'}); + testRotate(new char[] {'1', '2', '3'}, 2, new char[] {'2', '3', '1'}); + testRotate(new char[] {'1', '2', '3'}, 3, new char[] {'1', '2', '3'}); + testRotate(new char[] {'1', '2', '3'}, 4, new char[] {'3', '1', '2'}); + testRotate(new char[] {'1', '2', '3'}, 5, new char[] {'2', '3', '1'}); + + testRotate(new char[] {'1', '2', '3', '4'}, -9, new char[] {'2', '3', '4', '1'}); + testRotate(new char[] {'1', '2', '3', '4'}, -5, new char[] {'2', '3', '4', '1'}); + testRotate(new char[] {'1', '2', '3', '4'}, -1, new char[] {'2', '3', '4', '1'}); + testRotate(new char[] {'1', '2', '3', '4'}, 0, new char[] {'1', '2', '3', '4'}); + testRotate(new char[] {'1', '2', '3', '4'}, 1, new char[] {'4', '1', '2', '3'}); + testRotate(new char[] {'1', '2', '3', '4'}, 5, new char[] {'4', '1', '2', '3'}); + testRotate(new char[] {'1', '2', '3', '4'}, 9, new char[] {'4', '1', '2', '3'}); + + testRotate(new char[] {'1', '2', '3', '4', '5'}, -6, new char[] {'2', '3', '4', '5', '1'}); + testRotate(new char[] {'1', '2', '3', '4', '5'}, -4, new char[] {'5', '1', '2', '3', '4'}); + testRotate(new char[] {'1', '2', '3', '4', '5'}, -3, new char[] {'4', '5', '1', '2', '3'}); + testRotate(new char[] {'1', '2', '3', '4', '5'}, -1, new char[] {'2', '3', '4', '5', '1'}); + testRotate(new char[] {'1', '2', '3', '4', '5'}, 0, new char[] {'1', '2', '3', '4', '5'}); + testRotate(new char[] {'1', '2', '3', '4', '5'}, 1, new char[] {'5', '1', '2', '3', '4'}); + testRotate(new char[] {'1', '2', '3', '4', '5'}, 3, new char[] {'3', '4', '5', '1', '2'}); + testRotate(new char[] {'1', '2', '3', '4', '5'}, 4, new char[] {'2', '3', '4', '5', '1'}); + testRotate(new char[] {'1', '2', '3', '4', '5'}, 6, new char[] {'5', '1', '2', '3', '4'}); + } + + public void testRotateIndexed() { + testRotate(new char[] {}, 0, 0, 0, new char[] {}); + + testRotate(new char[] {'1'}, 0, 0, 1, new char[] {'1'}); + testRotate(new char[] {'1'}, 1, 0, 1, new char[] {'1'}); + testRotate(new char[] {'1'}, 1, 1, 1, new char[] {'1'}); + + // Rotate the central 5 elements, leaving the ends as-is + testRotate( + new char[] {'0', '1', '2', '3', '4', '5', '6'}, + -6, + 1, + 6, + new char[] {'0', '2', '3', '4', '5', '1', '6'}); + testRotate( + new char[] {'0', '1', '2', '3', '4', '5', '6'}, + -1, + 1, + 6, + new char[] {'0', '2', '3', '4', '5', '1', '6'}); + testRotate( + new char[] {'0', '1', '2', '3', '4', '5', '6'}, + 0, + 1, + 6, + new char[] {'0', '1', '2', '3', '4', '5', '6'}); + testRotate( + new char[] {'0', '1', '2', '3', '4', '5', '6'}, + 5, + 1, + 6, + new char[] {'0', '1', '2', '3', '4', '5', '6'}); + testRotate( + new char[] {'0', '1', '2', '3', '4', '5', '6'}, + 14, + 1, + 6, + new char[] {'0', '2', '3', '4', '5', '1', '6'}); + + // Rotate the first three elements + testRotate( + new char[] {'0', '1', '2', '3', '4', '5', '6'}, + -2, + 0, + 3, + new char[] {'2', '0', '1', '3', '4', '5', '6'}); + testRotate( + new char[] {'0', '1', '2', '3', '4', '5', '6'}, + -1, + 0, + 3, + new char[] {'1', '2', '0', '3', '4', '5', '6'}); + testRotate( + new char[] {'0', '1', '2', '3', '4', '5', '6'}, + 0, + 0, + 3, + new char[] {'0', '1', '2', '3', '4', '5', '6'}); + testRotate( + new char[] {'0', '1', '2', '3', '4', '5', '6'}, + 1, + 0, + 3, + new char[] {'2', '0', '1', '3', '4', '5', '6'}); + testRotate( + new char[] {'0', '1', '2', '3', '4', '5', '6'}, + 2, + 0, + 3, + new char[] {'1', '2', '0', '3', '4', '5', '6'}); + + // Rotate the last four elements + testRotate( + new char[] {'0', '1', '2', '3', '4', '5', '6'}, + -6, + 3, + 7, + new char[] {'0', '1', '2', '5', '6', '3', '4'}); + testRotate( + new char[] {'0', '1', '2', '3', '4', '5', '6'}, + -5, + 3, + 7, + new char[] {'0', '1', '2', '4', '5', '6', '3'}); + testRotate( + new char[] {'0', '1', '2', '3', '4', '5', '6'}, + -4, + 3, + 7, + new char[] {'0', '1', '2', '3', '4', '5', '6'}); + testRotate( + new char[] {'0', '1', '2', '3', '4', '5', '6'}, + -3, + 3, + 7, + new char[] {'0', '1', '2', '6', '3', '4', '5'}); + testRotate( + new char[] {'0', '1', '2', '3', '4', '5', '6'}, + -2, + 3, + 7, + new char[] {'0', '1', '2', '5', '6', '3', '4'}); + testRotate( + new char[] {'0', '1', '2', '3', '4', '5', '6'}, + -1, + 3, + 7, + new char[] {'0', '1', '2', '4', '5', '6', '3'}); + testRotate( + new char[] {'0', '1', '2', '3', '4', '5', '6'}, + 0, + 3, + 7, + new char[] {'0', '1', '2', '3', '4', '5', '6'}); + testRotate( + new char[] {'0', '1', '2', '3', '4', '5', '6'}, + 1, + 3, + 7, + new char[] {'0', '1', '2', '6', '3', '4', '5'}); + testRotate( + new char[] {'0', '1', '2', '3', '4', '5', '6'}, + 2, + 3, + 7, + new char[] {'0', '1', '2', '5', '6', '3', '4'}); + testRotate( + new char[] {'0', '1', '2', '3', '4', '5', '6'}, + 3, + 3, + 7, + new char[] {'0', '1', '2', '4', '5', '6', '3'}); + } + public void testSortDescending() { testSortDescending(new char[] {}, new char[] {}); testSortDescending(new char[] {'1'}, new char[] {'1'}); diff --git a/android/guava-tests/test/com/google/common/primitives/DoublesTest.java b/android/guava-tests/test/com/google/common/primitives/DoublesTest.java index 930c2e420a57..f4860f5601e0 100644 --- a/android/guava-tests/test/com/google/common/primitives/DoublesTest.java +++ b/android/guava-tests/test/com/google/common/primitives/DoublesTest.java @@ -365,6 +365,103 @@ public void testReverseIndexed() { testReverse(new double[] {-1, 1, -2, 2}, 1, 3, new double[] {-1, -2, 1, 2}); } + private static void testRotate(double[] input, int distance, double[] expectedOutput) { + input = Arrays.copyOf(input, input.length); + Doubles.rotate(input, distance); + assertThat(input).isEqualTo(expectedOutput); + } + + private static void testRotate( + double[] input, int distance, int fromIndex, int toIndex, double[] expectedOutput) { + input = Arrays.copyOf(input, input.length); + Doubles.rotate(input, distance, fromIndex, toIndex); + assertThat(input).isEqualTo(expectedOutput); + } + + public void testRotate() { + testRotate(new double[] {}, -1, new double[] {}); + testRotate(new double[] {}, 0, new double[] {}); + testRotate(new double[] {}, 1, new double[] {}); + + testRotate(new double[] {1}, -2, new double[] {1}); + testRotate(new double[] {1}, -1, new double[] {1}); + testRotate(new double[] {1}, 0, new double[] {1}); + testRotate(new double[] {1}, 1, new double[] {1}); + testRotate(new double[] {1}, 2, new double[] {1}); + + testRotate(new double[] {1, 2}, -3, new double[] {2, 1}); + testRotate(new double[] {1, 2}, -1, new double[] {2, 1}); + testRotate(new double[] {1, 2}, -2, new double[] {1, 2}); + testRotate(new double[] {1, 2}, 0, new double[] {1, 2}); + testRotate(new double[] {1, 2}, 1, new double[] {2, 1}); + testRotate(new double[] {1, 2}, 2, new double[] {1, 2}); + testRotate(new double[] {1, 2}, 3, new double[] {2, 1}); + + testRotate(new double[] {1, 2, 3}, -5, new double[] {3, 1, 2}); + testRotate(new double[] {1, 2, 3}, -4, new double[] {2, 3, 1}); + testRotate(new double[] {1, 2, 3}, -3, new double[] {1, 2, 3}); + testRotate(new double[] {1, 2, 3}, -2, new double[] {3, 1, 2}); + testRotate(new double[] {1, 2, 3}, -1, new double[] {2, 3, 1}); + testRotate(new double[] {1, 2, 3}, 0, new double[] {1, 2, 3}); + testRotate(new double[] {1, 2, 3}, 1, new double[] {3, 1, 2}); + testRotate(new double[] {1, 2, 3}, 2, new double[] {2, 3, 1}); + testRotate(new double[] {1, 2, 3}, 3, new double[] {1, 2, 3}); + testRotate(new double[] {1, 2, 3}, 4, new double[] {3, 1, 2}); + testRotate(new double[] {1, 2, 3}, 5, new double[] {2, 3, 1}); + + testRotate(new double[] {1, 2, 3, 4}, -9, new double[] {2, 3, 4, 1}); + testRotate(new double[] {1, 2, 3, 4}, -5, new double[] {2, 3, 4, 1}); + testRotate(new double[] {1, 2, 3, 4}, -1, new double[] {2, 3, 4, 1}); + testRotate(new double[] {1, 2, 3, 4}, 0, new double[] {1, 2, 3, 4}); + testRotate(new double[] {1, 2, 3, 4}, 1, new double[] {4, 1, 2, 3}); + testRotate(new double[] {1, 2, 3, 4}, 5, new double[] {4, 1, 2, 3}); + testRotate(new double[] {1, 2, 3, 4}, 9, new double[] {4, 1, 2, 3}); + + testRotate(new double[] {1, 2, 3, 4, 5}, -6, new double[] {2, 3, 4, 5, 1}); + testRotate(new double[] {1, 2, 3, 4, 5}, -4, new double[] {5, 1, 2, 3, 4}); + testRotate(new double[] {1, 2, 3, 4, 5}, -3, new double[] {4, 5, 1, 2, 3}); + testRotate(new double[] {1, 2, 3, 4, 5}, -1, new double[] {2, 3, 4, 5, 1}); + testRotate(new double[] {1, 2, 3, 4, 5}, 0, new double[] {1, 2, 3, 4, 5}); + testRotate(new double[] {1, 2, 3, 4, 5}, 1, new double[] {5, 1, 2, 3, 4}); + testRotate(new double[] {1, 2, 3, 4, 5}, 3, new double[] {3, 4, 5, 1, 2}); + testRotate(new double[] {1, 2, 3, 4, 5}, 4, new double[] {2, 3, 4, 5, 1}); + testRotate(new double[] {1, 2, 3, 4, 5}, 6, new double[] {5, 1, 2, 3, 4}); + } + + public void testRotateIndexed() { + testRotate(new double[] {}, 0, 0, 0, new double[] {}); + + testRotate(new double[] {1}, 0, 0, 1, new double[] {1}); + testRotate(new double[] {1}, 1, 0, 1, new double[] {1}); + testRotate(new double[] {1}, 1, 1, 1, new double[] {1}); + + // Rotate the central 5 elements, leaving the ends as-is + testRotate(new double[] {0, 1, 2, 3, 4, 5, 6}, -6, 1, 6, new double[] {0, 2, 3, 4, 5, 1, 6}); + testRotate(new double[] {0, 1, 2, 3, 4, 5, 6}, -1, 1, 6, new double[] {0, 2, 3, 4, 5, 1, 6}); + testRotate(new double[] {0, 1, 2, 3, 4, 5, 6}, 0, 1, 6, new double[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new double[] {0, 1, 2, 3, 4, 5, 6}, 5, 1, 6, new double[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new double[] {0, 1, 2, 3, 4, 5, 6}, 14, 1, 6, new double[] {0, 2, 3, 4, 5, 1, 6}); + + // Rotate the first three elements + testRotate(new double[] {0, 1, 2, 3, 4, 5, 6}, -2, 0, 3, new double[] {2, 0, 1, 3, 4, 5, 6}); + testRotate(new double[] {0, 1, 2, 3, 4, 5, 6}, -1, 0, 3, new double[] {1, 2, 0, 3, 4, 5, 6}); + testRotate(new double[] {0, 1, 2, 3, 4, 5, 6}, 0, 0, 3, new double[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new double[] {0, 1, 2, 3, 4, 5, 6}, 1, 0, 3, new double[] {2, 0, 1, 3, 4, 5, 6}); + testRotate(new double[] {0, 1, 2, 3, 4, 5, 6}, 2, 0, 3, new double[] {1, 2, 0, 3, 4, 5, 6}); + + // Rotate the last four elements + testRotate(new double[] {0, 1, 2, 3, 4, 5, 6}, -6, 3, 7, new double[] {0, 1, 2, 5, 6, 3, 4}); + testRotate(new double[] {0, 1, 2, 3, 4, 5, 6}, -5, 3, 7, new double[] {0, 1, 2, 4, 5, 6, 3}); + testRotate(new double[] {0, 1, 2, 3, 4, 5, 6}, -4, 3, 7, new double[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new double[] {0, 1, 2, 3, 4, 5, 6}, -3, 3, 7, new double[] {0, 1, 2, 6, 3, 4, 5}); + testRotate(new double[] {0, 1, 2, 3, 4, 5, 6}, -2, 3, 7, new double[] {0, 1, 2, 5, 6, 3, 4}); + testRotate(new double[] {0, 1, 2, 3, 4, 5, 6}, -1, 3, 7, new double[] {0, 1, 2, 4, 5, 6, 3}); + testRotate(new double[] {0, 1, 2, 3, 4, 5, 6}, 0, 3, 7, new double[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new double[] {0, 1, 2, 3, 4, 5, 6}, 1, 3, 7, new double[] {0, 1, 2, 6, 3, 4, 5}); + testRotate(new double[] {0, 1, 2, 3, 4, 5, 6}, 2, 3, 7, new double[] {0, 1, 2, 5, 6, 3, 4}); + testRotate(new double[] {0, 1, 2, 3, 4, 5, 6}, 3, 3, 7, new double[] {0, 1, 2, 4, 5, 6, 3}); + } + public void testSortDescending() { testSortDescending(new double[] {}, new double[] {}); testSortDescending(new double[] {1}, new double[] {1}); diff --git a/android/guava-tests/test/com/google/common/primitives/FloatsTest.java b/android/guava-tests/test/com/google/common/primitives/FloatsTest.java index 1833f26e28c1..75d5e8bff304 100644 --- a/android/guava-tests/test/com/google/common/primitives/FloatsTest.java +++ b/android/guava-tests/test/com/google/common/primitives/FloatsTest.java @@ -350,6 +350,103 @@ public void testReverseIndexed() { testReverse(new float[] {-1, 1, -2, 2}, 1, 3, new float[] {-1, -2, 1, 2}); } + private static void testRotate(float[] input, int distance, float[] expectedOutput) { + input = Arrays.copyOf(input, input.length); + Floats.rotate(input, distance); + assertThat(input).isEqualTo(expectedOutput); + } + + private static void testRotate( + float[] input, int distance, int fromIndex, int toIndex, float[] expectedOutput) { + input = Arrays.copyOf(input, input.length); + Floats.rotate(input, distance, fromIndex, toIndex); + assertThat(input).isEqualTo(expectedOutput); + } + + public void testRotate() { + testRotate(new float[] {}, -1, new float[] {}); + testRotate(new float[] {}, 0, new float[] {}); + testRotate(new float[] {}, 1, new float[] {}); + + testRotate(new float[] {1}, -2, new float[] {1}); + testRotate(new float[] {1}, -1, new float[] {1}); + testRotate(new float[] {1}, 0, new float[] {1}); + testRotate(new float[] {1}, 1, new float[] {1}); + testRotate(new float[] {1}, 2, new float[] {1}); + + testRotate(new float[] {1, 2}, -3, new float[] {2, 1}); + testRotate(new float[] {1, 2}, -1, new float[] {2, 1}); + testRotate(new float[] {1, 2}, -2, new float[] {1, 2}); + testRotate(new float[] {1, 2}, 0, new float[] {1, 2}); + testRotate(new float[] {1, 2}, 1, new float[] {2, 1}); + testRotate(new float[] {1, 2}, 2, new float[] {1, 2}); + testRotate(new float[] {1, 2}, 3, new float[] {2, 1}); + + testRotate(new float[] {1, 2, 3}, -5, new float[] {3, 1, 2}); + testRotate(new float[] {1, 2, 3}, -4, new float[] {2, 3, 1}); + testRotate(new float[] {1, 2, 3}, -3, new float[] {1, 2, 3}); + testRotate(new float[] {1, 2, 3}, -2, new float[] {3, 1, 2}); + testRotate(new float[] {1, 2, 3}, -1, new float[] {2, 3, 1}); + testRotate(new float[] {1, 2, 3}, 0, new float[] {1, 2, 3}); + testRotate(new float[] {1, 2, 3}, 1, new float[] {3, 1, 2}); + testRotate(new float[] {1, 2, 3}, 2, new float[] {2, 3, 1}); + testRotate(new float[] {1, 2, 3}, 3, new float[] {1, 2, 3}); + testRotate(new float[] {1, 2, 3}, 4, new float[] {3, 1, 2}); + testRotate(new float[] {1, 2, 3}, 5, new float[] {2, 3, 1}); + + testRotate(new float[] {1, 2, 3, 4}, -9, new float[] {2, 3, 4, 1}); + testRotate(new float[] {1, 2, 3, 4}, -5, new float[] {2, 3, 4, 1}); + testRotate(new float[] {1, 2, 3, 4}, -1, new float[] {2, 3, 4, 1}); + testRotate(new float[] {1, 2, 3, 4}, 0, new float[] {1, 2, 3, 4}); + testRotate(new float[] {1, 2, 3, 4}, 1, new float[] {4, 1, 2, 3}); + testRotate(new float[] {1, 2, 3, 4}, 5, new float[] {4, 1, 2, 3}); + testRotate(new float[] {1, 2, 3, 4}, 9, new float[] {4, 1, 2, 3}); + + testRotate(new float[] {1, 2, 3, 4, 5}, -6, new float[] {2, 3, 4, 5, 1}); + testRotate(new float[] {1, 2, 3, 4, 5}, -4, new float[] {5, 1, 2, 3, 4}); + testRotate(new float[] {1, 2, 3, 4, 5}, -3, new float[] {4, 5, 1, 2, 3}); + testRotate(new float[] {1, 2, 3, 4, 5}, -1, new float[] {2, 3, 4, 5, 1}); + testRotate(new float[] {1, 2, 3, 4, 5}, 0, new float[] {1, 2, 3, 4, 5}); + testRotate(new float[] {1, 2, 3, 4, 5}, 1, new float[] {5, 1, 2, 3, 4}); + testRotate(new float[] {1, 2, 3, 4, 5}, 3, new float[] {3, 4, 5, 1, 2}); + testRotate(new float[] {1, 2, 3, 4, 5}, 4, new float[] {2, 3, 4, 5, 1}); + testRotate(new float[] {1, 2, 3, 4, 5}, 6, new float[] {5, 1, 2, 3, 4}); + } + + public void testRotateIndexed() { + testRotate(new float[] {}, 0, 0, 0, new float[] {}); + + testRotate(new float[] {1}, 0, 0, 1, new float[] {1}); + testRotate(new float[] {1}, 1, 0, 1, new float[] {1}); + testRotate(new float[] {1}, 1, 1, 1, new float[] {1}); + + // Rotate the central 5 elements, leaving the ends as-is + testRotate(new float[] {0, 1, 2, 3, 4, 5, 6}, -6, 1, 6, new float[] {0, 2, 3, 4, 5, 1, 6}); + testRotate(new float[] {0, 1, 2, 3, 4, 5, 6}, -1, 1, 6, new float[] {0, 2, 3, 4, 5, 1, 6}); + testRotate(new float[] {0, 1, 2, 3, 4, 5, 6}, 0, 1, 6, new float[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new float[] {0, 1, 2, 3, 4, 5, 6}, 5, 1, 6, new float[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new float[] {0, 1, 2, 3, 4, 5, 6}, 14, 1, 6, new float[] {0, 2, 3, 4, 5, 1, 6}); + + // Rotate the first three elements + testRotate(new float[] {0, 1, 2, 3, 4, 5, 6}, -2, 0, 3, new float[] {2, 0, 1, 3, 4, 5, 6}); + testRotate(new float[] {0, 1, 2, 3, 4, 5, 6}, -1, 0, 3, new float[] {1, 2, 0, 3, 4, 5, 6}); + testRotate(new float[] {0, 1, 2, 3, 4, 5, 6}, 0, 0, 3, new float[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new float[] {0, 1, 2, 3, 4, 5, 6}, 1, 0, 3, new float[] {2, 0, 1, 3, 4, 5, 6}); + testRotate(new float[] {0, 1, 2, 3, 4, 5, 6}, 2, 0, 3, new float[] {1, 2, 0, 3, 4, 5, 6}); + + // Rotate the last four elements + testRotate(new float[] {0, 1, 2, 3, 4, 5, 6}, -6, 3, 7, new float[] {0, 1, 2, 5, 6, 3, 4}); + testRotate(new float[] {0, 1, 2, 3, 4, 5, 6}, -5, 3, 7, new float[] {0, 1, 2, 4, 5, 6, 3}); + testRotate(new float[] {0, 1, 2, 3, 4, 5, 6}, -4, 3, 7, new float[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new float[] {0, 1, 2, 3, 4, 5, 6}, -3, 3, 7, new float[] {0, 1, 2, 6, 3, 4, 5}); + testRotate(new float[] {0, 1, 2, 3, 4, 5, 6}, -2, 3, 7, new float[] {0, 1, 2, 5, 6, 3, 4}); + testRotate(new float[] {0, 1, 2, 3, 4, 5, 6}, -1, 3, 7, new float[] {0, 1, 2, 4, 5, 6, 3}); + testRotate(new float[] {0, 1, 2, 3, 4, 5, 6}, 0, 3, 7, new float[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new float[] {0, 1, 2, 3, 4, 5, 6}, 1, 3, 7, new float[] {0, 1, 2, 6, 3, 4, 5}); + testRotate(new float[] {0, 1, 2, 3, 4, 5, 6}, 2, 3, 7, new float[] {0, 1, 2, 5, 6, 3, 4}); + testRotate(new float[] {0, 1, 2, 3, 4, 5, 6}, 3, 3, 7, new float[] {0, 1, 2, 4, 5, 6, 3}); + } + public void testSortDescending() { testSortDescending(new float[] {}, new float[] {}); testSortDescending(new float[] {1}, new float[] {1}); diff --git a/android/guava-tests/test/com/google/common/primitives/LongsTest.java b/android/guava-tests/test/com/google/common/primitives/LongsTest.java index 401dc2a43ebe..938df8336a6c 100644 --- a/android/guava-tests/test/com/google/common/primitives/LongsTest.java +++ b/android/guava-tests/test/com/google/common/primitives/LongsTest.java @@ -354,6 +354,103 @@ public void testReverseIndexed() { testReverse(new long[] {-1, 1, -2, 2}, 1, 3, new long[] {-1, -2, 1, 2}); } + private static void testRotate(long[] input, int distance, long[] expectedOutput) { + input = Arrays.copyOf(input, input.length); + Longs.rotate(input, distance); + assertThat(input).isEqualTo(expectedOutput); + } + + private static void testRotate( + long[] input, int distance, int fromIndex, int toIndex, long[] expectedOutput) { + input = Arrays.copyOf(input, input.length); + Longs.rotate(input, distance, fromIndex, toIndex); + assertThat(input).isEqualTo(expectedOutput); + } + + public void testRotate() { + testRotate(new long[] {}, -1, new long[] {}); + testRotate(new long[] {}, 0, new long[] {}); + testRotate(new long[] {}, 1, new long[] {}); + + testRotate(new long[] {1}, -2, new long[] {1}); + testRotate(new long[] {1}, -1, new long[] {1}); + testRotate(new long[] {1}, 0, new long[] {1}); + testRotate(new long[] {1}, 1, new long[] {1}); + testRotate(new long[] {1}, 2, new long[] {1}); + + testRotate(new long[] {1, 2}, -3, new long[] {2, 1}); + testRotate(new long[] {1, 2}, -1, new long[] {2, 1}); + testRotate(new long[] {1, 2}, -2, new long[] {1, 2}); + testRotate(new long[] {1, 2}, 0, new long[] {1, 2}); + testRotate(new long[] {1, 2}, 1, new long[] {2, 1}); + testRotate(new long[] {1, 2}, 2, new long[] {1, 2}); + testRotate(new long[] {1, 2}, 3, new long[] {2, 1}); + + testRotate(new long[] {1, 2, 3}, -5, new long[] {3, 1, 2}); + testRotate(new long[] {1, 2, 3}, -4, new long[] {2, 3, 1}); + testRotate(new long[] {1, 2, 3}, -3, new long[] {1, 2, 3}); + testRotate(new long[] {1, 2, 3}, -2, new long[] {3, 1, 2}); + testRotate(new long[] {1, 2, 3}, -1, new long[] {2, 3, 1}); + testRotate(new long[] {1, 2, 3}, 0, new long[] {1, 2, 3}); + testRotate(new long[] {1, 2, 3}, 1, new long[] {3, 1, 2}); + testRotate(new long[] {1, 2, 3}, 2, new long[] {2, 3, 1}); + testRotate(new long[] {1, 2, 3}, 3, new long[] {1, 2, 3}); + testRotate(new long[] {1, 2, 3}, 4, new long[] {3, 1, 2}); + testRotate(new long[] {1, 2, 3}, 5, new long[] {2, 3, 1}); + + testRotate(new long[] {1, 2, 3, 4}, -9, new long[] {2, 3, 4, 1}); + testRotate(new long[] {1, 2, 3, 4}, -5, new long[] {2, 3, 4, 1}); + testRotate(new long[] {1, 2, 3, 4}, -1, new long[] {2, 3, 4, 1}); + testRotate(new long[] {1, 2, 3, 4}, 0, new long[] {1, 2, 3, 4}); + testRotate(new long[] {1, 2, 3, 4}, 1, new long[] {4, 1, 2, 3}); + testRotate(new long[] {1, 2, 3, 4}, 5, new long[] {4, 1, 2, 3}); + testRotate(new long[] {1, 2, 3, 4}, 9, new long[] {4, 1, 2, 3}); + + testRotate(new long[] {1, 2, 3, 4, 5}, -6, new long[] {2, 3, 4, 5, 1}); + testRotate(new long[] {1, 2, 3, 4, 5}, -4, new long[] {5, 1, 2, 3, 4}); + testRotate(new long[] {1, 2, 3, 4, 5}, -3, new long[] {4, 5, 1, 2, 3}); + testRotate(new long[] {1, 2, 3, 4, 5}, -1, new long[] {2, 3, 4, 5, 1}); + testRotate(new long[] {1, 2, 3, 4, 5}, 0, new long[] {1, 2, 3, 4, 5}); + testRotate(new long[] {1, 2, 3, 4, 5}, 1, new long[] {5, 1, 2, 3, 4}); + testRotate(new long[] {1, 2, 3, 4, 5}, 3, new long[] {3, 4, 5, 1, 2}); + testRotate(new long[] {1, 2, 3, 4, 5}, 4, new long[] {2, 3, 4, 5, 1}); + testRotate(new long[] {1, 2, 3, 4, 5}, 6, new long[] {5, 1, 2, 3, 4}); + } + + public void testRotateIndexed() { + testRotate(new long[] {}, 0, 0, 0, new long[] {}); + + testRotate(new long[] {1}, 0, 0, 1, new long[] {1}); + testRotate(new long[] {1}, 1, 0, 1, new long[] {1}); + testRotate(new long[] {1}, 1, 1, 1, new long[] {1}); + + // Rotate the central 5 elements, leaving the ends as-is + testRotate(new long[] {0, 1, 2, 3, 4, 5, 6}, -6, 1, 6, new long[] {0, 2, 3, 4, 5, 1, 6}); + testRotate(new long[] {0, 1, 2, 3, 4, 5, 6}, -1, 1, 6, new long[] {0, 2, 3, 4, 5, 1, 6}); + testRotate(new long[] {0, 1, 2, 3, 4, 5, 6}, 0, 1, 6, new long[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new long[] {0, 1, 2, 3, 4, 5, 6}, 5, 1, 6, new long[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new long[] {0, 1, 2, 3, 4, 5, 6}, 14, 1, 6, new long[] {0, 2, 3, 4, 5, 1, 6}); + + // Rotate the first three elements + testRotate(new long[] {0, 1, 2, 3, 4, 5, 6}, -2, 0, 3, new long[] {2, 0, 1, 3, 4, 5, 6}); + testRotate(new long[] {0, 1, 2, 3, 4, 5, 6}, -1, 0, 3, new long[] {1, 2, 0, 3, 4, 5, 6}); + testRotate(new long[] {0, 1, 2, 3, 4, 5, 6}, 0, 0, 3, new long[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new long[] {0, 1, 2, 3, 4, 5, 6}, 1, 0, 3, new long[] {2, 0, 1, 3, 4, 5, 6}); + testRotate(new long[] {0, 1, 2, 3, 4, 5, 6}, 2, 0, 3, new long[] {1, 2, 0, 3, 4, 5, 6}); + + // Rotate the last four elements + testRotate(new long[] {0, 1, 2, 3, 4, 5, 6}, -6, 3, 7, new long[] {0, 1, 2, 5, 6, 3, 4}); + testRotate(new long[] {0, 1, 2, 3, 4, 5, 6}, -5, 3, 7, new long[] {0, 1, 2, 4, 5, 6, 3}); + testRotate(new long[] {0, 1, 2, 3, 4, 5, 6}, -4, 3, 7, new long[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new long[] {0, 1, 2, 3, 4, 5, 6}, -3, 3, 7, new long[] {0, 1, 2, 6, 3, 4, 5}); + testRotate(new long[] {0, 1, 2, 3, 4, 5, 6}, -2, 3, 7, new long[] {0, 1, 2, 5, 6, 3, 4}); + testRotate(new long[] {0, 1, 2, 3, 4, 5, 6}, -1, 3, 7, new long[] {0, 1, 2, 4, 5, 6, 3}); + testRotate(new long[] {0, 1, 2, 3, 4, 5, 6}, 0, 3, 7, new long[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new long[] {0, 1, 2, 3, 4, 5, 6}, 1, 3, 7, new long[] {0, 1, 2, 6, 3, 4, 5}); + testRotate(new long[] {0, 1, 2, 3, 4, 5, 6}, 2, 3, 7, new long[] {0, 1, 2, 5, 6, 3, 4}); + testRotate(new long[] {0, 1, 2, 3, 4, 5, 6}, 3, 3, 7, new long[] {0, 1, 2, 4, 5, 6, 3}); + } + public void testSortDescending() { testSortDescending(new long[] {}, new long[] {}); testSortDescending(new long[] {1}, new long[] {1}); diff --git a/android/guava-tests/test/com/google/common/primitives/ShortsTest.java b/android/guava-tests/test/com/google/common/primitives/ShortsTest.java index 287974ae87ba..f7e534659432 100644 --- a/android/guava-tests/test/com/google/common/primitives/ShortsTest.java +++ b/android/guava-tests/test/com/google/common/primitives/ShortsTest.java @@ -366,6 +366,103 @@ public void testReverseIndexed() { testReverse(new short[] {-1, 1, -2, 2}, 1, 3, new short[] {-1, -2, 1, 2}); } + private static void testRotate(short[] input, int distance, short[] expectedOutput) { + input = Arrays.copyOf(input, input.length); + Shorts.rotate(input, distance); + assertThat(input).isEqualTo(expectedOutput); + } + + private static void testRotate( + short[] input, int distance, int fromIndex, int toIndex, short[] expectedOutput) { + input = Arrays.copyOf(input, input.length); + Shorts.rotate(input, distance, fromIndex, toIndex); + assertThat(input).isEqualTo(expectedOutput); + } + + public void testRotate() { + testRotate(new short[] {}, -1, new short[] {}); + testRotate(new short[] {}, 0, new short[] {}); + testRotate(new short[] {}, 1, new short[] {}); + + testRotate(new short[] {1}, -2, new short[] {1}); + testRotate(new short[] {1}, -1, new short[] {1}); + testRotate(new short[] {1}, 0, new short[] {1}); + testRotate(new short[] {1}, 1, new short[] {1}); + testRotate(new short[] {1}, 2, new short[] {1}); + + testRotate(new short[] {1, 2}, -3, new short[] {2, 1}); + testRotate(new short[] {1, 2}, -1, new short[] {2, 1}); + testRotate(new short[] {1, 2}, -2, new short[] {1, 2}); + testRotate(new short[] {1, 2}, 0, new short[] {1, 2}); + testRotate(new short[] {1, 2}, 1, new short[] {2, 1}); + testRotate(new short[] {1, 2}, 2, new short[] {1, 2}); + testRotate(new short[] {1, 2}, 3, new short[] {2, 1}); + + testRotate(new short[] {1, 2, 3}, -5, new short[] {3, 1, 2}); + testRotate(new short[] {1, 2, 3}, -4, new short[] {2, 3, 1}); + testRotate(new short[] {1, 2, 3}, -3, new short[] {1, 2, 3}); + testRotate(new short[] {1, 2, 3}, -2, new short[] {3, 1, 2}); + testRotate(new short[] {1, 2, 3}, -1, new short[] {2, 3, 1}); + testRotate(new short[] {1, 2, 3}, 0, new short[] {1, 2, 3}); + testRotate(new short[] {1, 2, 3}, 1, new short[] {3, 1, 2}); + testRotate(new short[] {1, 2, 3}, 2, new short[] {2, 3, 1}); + testRotate(new short[] {1, 2, 3}, 3, new short[] {1, 2, 3}); + testRotate(new short[] {1, 2, 3}, 4, new short[] {3, 1, 2}); + testRotate(new short[] {1, 2, 3}, 5, new short[] {2, 3, 1}); + + testRotate(new short[] {1, 2, 3, 4}, -9, new short[] {2, 3, 4, 1}); + testRotate(new short[] {1, 2, 3, 4}, -5, new short[] {2, 3, 4, 1}); + testRotate(new short[] {1, 2, 3, 4}, -1, new short[] {2, 3, 4, 1}); + testRotate(new short[] {1, 2, 3, 4}, 0, new short[] {1, 2, 3, 4}); + testRotate(new short[] {1, 2, 3, 4}, 1, new short[] {4, 1, 2, 3}); + testRotate(new short[] {1, 2, 3, 4}, 5, new short[] {4, 1, 2, 3}); + testRotate(new short[] {1, 2, 3, 4}, 9, new short[] {4, 1, 2, 3}); + + testRotate(new short[] {1, 2, 3, 4, 5}, -6, new short[] {2, 3, 4, 5, 1}); + testRotate(new short[] {1, 2, 3, 4, 5}, -4, new short[] {5, 1, 2, 3, 4}); + testRotate(new short[] {1, 2, 3, 4, 5}, -3, new short[] {4, 5, 1, 2, 3}); + testRotate(new short[] {1, 2, 3, 4, 5}, -1, new short[] {2, 3, 4, 5, 1}); + testRotate(new short[] {1, 2, 3, 4, 5}, 0, new short[] {1, 2, 3, 4, 5}); + testRotate(new short[] {1, 2, 3, 4, 5}, 1, new short[] {5, 1, 2, 3, 4}); + testRotate(new short[] {1, 2, 3, 4, 5}, 3, new short[] {3, 4, 5, 1, 2}); + testRotate(new short[] {1, 2, 3, 4, 5}, 4, new short[] {2, 3, 4, 5, 1}); + testRotate(new short[] {1, 2, 3, 4, 5}, 6, new short[] {5, 1, 2, 3, 4}); + } + + public void testRotateIndexed() { + testRotate(new short[] {}, 0, 0, 0, new short[] {}); + + testRotate(new short[] {1}, 0, 0, 1, new short[] {1}); + testRotate(new short[] {1}, 1, 0, 1, new short[] {1}); + testRotate(new short[] {1}, 1, 1, 1, new short[] {1}); + + // Rotate the central 5 elements, leaving the ends as-is + testRotate(new short[] {0, 1, 2, 3, 4, 5, 6}, -6, 1, 6, new short[] {0, 2, 3, 4, 5, 1, 6}); + testRotate(new short[] {0, 1, 2, 3, 4, 5, 6}, -1, 1, 6, new short[] {0, 2, 3, 4, 5, 1, 6}); + testRotate(new short[] {0, 1, 2, 3, 4, 5, 6}, 0, 1, 6, new short[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new short[] {0, 1, 2, 3, 4, 5, 6}, 5, 1, 6, new short[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new short[] {0, 1, 2, 3, 4, 5, 6}, 14, 1, 6, new short[] {0, 2, 3, 4, 5, 1, 6}); + + // Rotate the first three elements + testRotate(new short[] {0, 1, 2, 3, 4, 5, 6}, -2, 0, 3, new short[] {2, 0, 1, 3, 4, 5, 6}); + testRotate(new short[] {0, 1, 2, 3, 4, 5, 6}, -1, 0, 3, new short[] {1, 2, 0, 3, 4, 5, 6}); + testRotate(new short[] {0, 1, 2, 3, 4, 5, 6}, 0, 0, 3, new short[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new short[] {0, 1, 2, 3, 4, 5, 6}, 1, 0, 3, new short[] {2, 0, 1, 3, 4, 5, 6}); + testRotate(new short[] {0, 1, 2, 3, 4, 5, 6}, 2, 0, 3, new short[] {1, 2, 0, 3, 4, 5, 6}); + + // Rotate the last four elements + testRotate(new short[] {0, 1, 2, 3, 4, 5, 6}, -6, 3, 7, new short[] {0, 1, 2, 5, 6, 3, 4}); + testRotate(new short[] {0, 1, 2, 3, 4, 5, 6}, -5, 3, 7, new short[] {0, 1, 2, 4, 5, 6, 3}); + testRotate(new short[] {0, 1, 2, 3, 4, 5, 6}, -4, 3, 7, new short[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new short[] {0, 1, 2, 3, 4, 5, 6}, -3, 3, 7, new short[] {0, 1, 2, 6, 3, 4, 5}); + testRotate(new short[] {0, 1, 2, 3, 4, 5, 6}, -2, 3, 7, new short[] {0, 1, 2, 5, 6, 3, 4}); + testRotate(new short[] {0, 1, 2, 3, 4, 5, 6}, -1, 3, 7, new short[] {0, 1, 2, 4, 5, 6, 3}); + testRotate(new short[] {0, 1, 2, 3, 4, 5, 6}, 0, 3, 7, new short[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new short[] {0, 1, 2, 3, 4, 5, 6}, 1, 3, 7, new short[] {0, 1, 2, 6, 3, 4, 5}); + testRotate(new short[] {0, 1, 2, 3, 4, 5, 6}, 2, 3, 7, new short[] {0, 1, 2, 5, 6, 3, 4}); + testRotate(new short[] {0, 1, 2, 3, 4, 5, 6}, 3, 3, 7, new short[] {0, 1, 2, 4, 5, 6, 3}); + } + public void testSortDescending() { testSortDescending(new short[] {}, new short[] {}); testSortDescending(new short[] {1}, new short[] {1}); diff --git a/android/guava/src/com/google/common/primitives/Booleans.java b/android/guava/src/com/google/common/primitives/Booleans.java index 522049bfe3eb..e637550ad91b 100644 --- a/android/guava/src/com/google/common/primitives/Booleans.java +++ b/android/guava/src/com/google/common/primitives/Booleans.java @@ -551,4 +551,54 @@ public static void reverse(boolean[] array, int fromIndex, int toIndex) { array[j] = tmp; } } + + /** + * Performs a right rotation of {@code array} of "distance" places, so that the first element is + * moved to index "distance", and the element at index {@code i} ends up at index {@code (distance + * + i) mod array.length}. This is equivalent to {@code Collections.rotate(Booleans.asList(array), + * distance)}, but is somewhat faster. + * + *

The provided "distance" may be negative, which will rotate left. + * + * @since NEXT + */ + public static void rotate(boolean[] array, int distance) { + rotate(array, distance, 0, array.length); + } + + /** + * Performs a right rotation of {@code array} between {@code fromIndex} inclusive and {@code + * toIndex} exclusive. This is equivalent to {@code + * Collections.rotate(Booleans.asList(array).subList(fromIndex, toIndex), distance)}, but is + * somewhat faster. + * + *

The provided "distance" may be negative, which will rotate left. + * + * @throws IndexOutOfBoundsException if {@code fromIndex < 0}, {@code toIndex > array.length}, or + * {@code toIndex > fromIndex} + * @since NEXT + */ + public static void rotate(boolean[] array, int distance, int fromIndex, int toIndex) { + // See Ints.rotate for more details about possible algorithms here. + checkNotNull(array); + checkPositionIndexes(fromIndex, toIndex, array.length); + if (array.length <= 1) { + return; + } + + int length = toIndex - fromIndex; + // Obtain m = (-distance mod length), a non-negative value less than "length". This is how many + // places left to rotate. + int m = -distance % length; + m = (m < 0) ? m + length : m; + // The current index of what will become the first element of the rotated section. + int newFirstIndex = m + fromIndex; + if (newFirstIndex == fromIndex) { + return; + } + + reverse(array, fromIndex, newFirstIndex); + reverse(array, newFirstIndex, toIndex); + reverse(array, fromIndex, toIndex); + } } diff --git a/android/guava/src/com/google/common/primitives/Bytes.java b/android/guava/src/com/google/common/primitives/Bytes.java index 62997f34aac7..11832742e5fa 100644 --- a/android/guava/src/com/google/common/primitives/Bytes.java +++ b/android/guava/src/com/google/common/primitives/Bytes.java @@ -396,4 +396,54 @@ public static void reverse(byte[] array, int fromIndex, int toIndex) { array[j] = tmp; } } + + /** + * Performs a right rotation of {@code array} of "distance" places, so that the first element is + * moved to index "distance", and the element at index {@code i} ends up at index {@code (distance + * + i) mod array.length}. This is equivalent to {@code Collections.rotate(Bytes.asList(array), + * distance)}, but is somewhat faster. + * + *

The provided "distance" may be negative, which will rotate left. + * + * @since NEXT + */ + public static void rotate(byte[] array, int distance) { + rotate(array, distance, 0, array.length); + } + + /** + * Performs a right rotation of {@code array} between {@code fromIndex} inclusive and {@code + * toIndex} exclusive. This is equivalent to {@code + * Collections.rotate(Bytes.asList(array).subList(fromIndex, toIndex), distance)}, but is somewhat + * faster. + * + *

The provided "distance" may be negative, which will rotate left. + * + * @throws IndexOutOfBoundsException if {@code fromIndex < 0}, {@code toIndex > array.length}, or + * {@code toIndex > fromIndex} + * @since NEXT + */ + public static void rotate(byte[] array, int distance, int fromIndex, int toIndex) { + // See Ints.rotate for more details about possible algorithms here. + checkNotNull(array); + checkPositionIndexes(fromIndex, toIndex, array.length); + if (array.length <= 1) { + return; + } + + int length = toIndex - fromIndex; + // Obtain m = (-distance mod length), a non-negative value less than "length". This is how many + // places left to rotate. + int m = -distance % length; + m = (m < 0) ? m + length : m; + // The current index of what will become the first element of the rotated section. + int newFirstIndex = m + fromIndex; + if (newFirstIndex == fromIndex) { + return; + } + + reverse(array, fromIndex, newFirstIndex); + reverse(array, newFirstIndex, toIndex); + reverse(array, fromIndex, toIndex); + } } diff --git a/android/guava/src/com/google/common/primitives/Chars.java b/android/guava/src/com/google/common/primitives/Chars.java index 4a2e3a3449cb..c677021f1aa4 100644 --- a/android/guava/src/com/google/common/primitives/Chars.java +++ b/android/guava/src/com/google/common/primitives/Chars.java @@ -488,6 +488,56 @@ public static void reverse(char[] array, int fromIndex, int toIndex) { } } + /** + * Performs a right rotation of {@code array} of "distance" places, so that the first element is + * moved to index "distance", and the element at index {@code i} ends up at index {@code (distance + * + i) mod array.length}. This is equivalent to {@code Collections.rotate(Chars.asList(array), + * distance)}, but is considerably faster and avoids allocation and garbage collection. + * + *

The provided "distance" may be negative, which will rotate left. + * + * @since NEXT + */ + public static void rotate(char[] array, int distance) { + rotate(array, distance, 0, array.length); + } + + /** + * Performs a right rotation of {@code array} between {@code fromIndex} inclusive and {@code + * toIndex} exclusive. This is equivalent to {@code + * Collections.rotate(Chars.asList(array).subList(fromIndex, toIndex), distance)}, but is + * considerably faster and avoids allocations and garbage collection. + * + *

The provided "distance" may be negative, which will rotate left. + * + * @throws IndexOutOfBoundsException if {@code fromIndex < 0}, {@code toIndex > array.length}, or + * {@code toIndex > fromIndex} + * @since NEXT + */ + public static void rotate(char[] array, int distance, int fromIndex, int toIndex) { + // See Ints.rotate for more details about possible algorithms here. + checkNotNull(array); + checkPositionIndexes(fromIndex, toIndex, array.length); + if (array.length <= 1) { + return; + } + + int length = toIndex - fromIndex; + // Obtain m = (-distance mod length), a non-negative value less than "length". This is how many + // places left to rotate. + int m = -distance % length; + m = (m < 0) ? m + length : m; + // The current index of what will become the first element of the rotated section. + int newFirstIndex = m + fromIndex; + if (newFirstIndex == fromIndex) { + return; + } + + reverse(array, fromIndex, newFirstIndex); + reverse(array, newFirstIndex, toIndex); + reverse(array, fromIndex, toIndex); + } + /** * Returns a fixed-size list backed by the specified array, similar to {@link * Arrays#asList(Object[])}. The list supports {@link List#set(int, Object)}, but any attempt to diff --git a/android/guava/src/com/google/common/primitives/Doubles.java b/android/guava/src/com/google/common/primitives/Doubles.java index ce5df2e394db..c6bf2f138723 100644 --- a/android/guava/src/com/google/common/primitives/Doubles.java +++ b/android/guava/src/com/google/common/primitives/Doubles.java @@ -467,6 +467,56 @@ public static void reverse(double[] array, int fromIndex, int toIndex) { } } + /** + * Performs a right rotation of {@code array} of "distance" places, so that the first element is + * moved to index "distance", and the element at index {@code i} ends up at index {@code (distance + * + i) mod array.length}. This is equivalent to {@code Collections.rotate(Bytes.asList(array), + * distance)}, but is considerably faster and avoids allocation and garbage collection. + * + *

The provided "distance" may be negative, which will rotate left. + * + * @since NEXT + */ + public static void rotate(double[] array, int distance) { + rotate(array, distance, 0, array.length); + } + + /** + * Performs a right rotation of {@code array} between {@code fromIndex} inclusive and {@code + * toIndex} exclusive. This is equivalent to {@code + * Collections.rotate(Bytes.asList(array).subList(fromIndex, toIndex), distance)}, but is + * considerably faster and avoids allocations and garbage collection. + * + *

The provided "distance" may be negative, which will rotate left. + * + * @throws IndexOutOfBoundsException if {@code fromIndex < 0}, {@code toIndex > array.length}, or + * {@code toIndex > fromIndex} + * @since NEXT + */ + public static void rotate(double[] array, int distance, int fromIndex, int toIndex) { + // See Ints.rotate for more details about possible algorithms here. + checkNotNull(array); + checkPositionIndexes(fromIndex, toIndex, array.length); + if (array.length <= 1) { + return; + } + + int length = toIndex - fromIndex; + // Obtain m = (-distance mod length), a non-negative value less than "length". This is how many + // places left to rotate. + int m = -distance % length; + m = (m < 0) ? m + length : m; + // The current index of what will become the first element of the rotated section. + int newFirstIndex = m + fromIndex; + if (newFirstIndex == fromIndex) { + return; + } + + reverse(array, fromIndex, newFirstIndex); + reverse(array, newFirstIndex, toIndex); + reverse(array, fromIndex, toIndex); + } + /** * Returns an array containing each value of {@code collection}, converted to a {@code double} * value in the manner of {@link Number#doubleValue}. diff --git a/android/guava/src/com/google/common/primitives/Floats.java b/android/guava/src/com/google/common/primitives/Floats.java index b038cb289636..a9a44d6e3532 100644 --- a/android/guava/src/com/google/common/primitives/Floats.java +++ b/android/guava/src/com/google/common/primitives/Floats.java @@ -464,6 +464,56 @@ public static void reverse(float[] array, int fromIndex, int toIndex) { } } + /** + * Performs a right rotation of {@code array} of "distance" places, so that the first element is + * moved to index "distance", and the element at index {@code i} ends up at index {@code (distance + * + i) mod array.length}. This is equivalent to {@code Collections.rotate(Floats.asList(array), + * distance)}, but is considerably faster and avoids allocation and garbage collection. + * + *

The provided "distance" may be negative, which will rotate left. + * + * @since NEXT + */ + public static void rotate(float[] array, int distance) { + rotate(array, distance, 0, array.length); + } + + /** + * Performs a right rotation of {@code array} between {@code fromIndex} inclusive and {@code + * toIndex} exclusive. This is equivalent to {@code + * Collections.rotate(Floats.asList(array).subList(fromIndex, toIndex), distance)}, but is + * considerably faster and avoids allocations and garbage collection. + * + *

The provided "distance" may be negative, which will rotate left. + * + * @throws IndexOutOfBoundsException if {@code fromIndex < 0}, {@code toIndex > array.length}, or + * {@code toIndex > fromIndex} + * @since NEXT + */ + public static void rotate(float[] array, int distance, int fromIndex, int toIndex) { + // See Ints.rotate for more details about possible algorithms here. + checkNotNull(array); + checkPositionIndexes(fromIndex, toIndex, array.length); + if (array.length <= 1) { + return; + } + + int length = toIndex - fromIndex; + // Obtain m = (-distance mod length), a non-negative value less than "length". This is how many + // places left to rotate. + int m = -distance % length; + m = (m < 0) ? m + length : m; + // The current index of what will become the first element of the rotated section. + int newFirstIndex = m + fromIndex; + if (newFirstIndex == fromIndex) { + return; + } + + reverse(array, fromIndex, newFirstIndex); + reverse(array, newFirstIndex, toIndex); + reverse(array, fromIndex, toIndex); + } + /** * Returns an array containing each value of {@code collection}, converted to a {@code float} * value in the manner of {@link Number#floatValue}. diff --git a/android/guava/src/com/google/common/primitives/Ints.java b/android/guava/src/com/google/common/primitives/Ints.java index 383b292b36fb..f840e9042db3 100644 --- a/android/guava/src/com/google/common/primitives/Ints.java +++ b/android/guava/src/com/google/common/primitives/Ints.java @@ -537,6 +537,33 @@ public static void rotate(int[] array, int distance) { * @since NEXT */ public static void rotate(int[] array, int distance, int fromIndex, int toIndex) { + // There are several well-known algorithms for rotating part of an array (or, equivalently, + // exchanging two blocks of memory). This classic text by Gries and Mills mentions several: + // https://ecommons.cornell.edu/bitstream/handle/1813/6292/81-452.pdf. + // (1) "Reversal", the one we have here. + // (2) "Dolphin". If we're rotating an array a of size n by a distance of d, then element a[0] + // ends up at a[d], which in turn ends up at a[2d], and so on until we get back to a[0]. + // (All indices taken mod n.) If d and n are mutually prime, all elements will have been + // moved at that point. Otherwise, we can rotate the cycle a[1], a[1 + d], a[1 + 2d], etc, + // then a[2] etc, and so on until we have rotated all elements. There are gcd(d, n) cycles + // in all. + // (3) "Successive". We can consider that we are exchanging a block of size d (a[0..d-1]) with a + // block of size n-d (a[d..n-1]), where in general these blocks have different sizes. If we + // imagine a line separating the first block from the second, we can proceed by exchanging + // the smaller of these blocks with the far end of the other one. That leaves us with a + // smaller version of the same problem. + // Say we are rotating abcdefgh by 5. We start with abcde|fgh. The smaller block is [fgh]: + // [abc]de|[fgh] -> [fgh]de|[abc]. Now [fgh] is in the right place, but we need to swap [de] + // with [abc]: fgh[de]|a[bc] -> fgh[bc]|a[de]. Now we need to swap [a] with [bc]: + // fgh[b]c|[a]de -> fgh[a]c|[b]de. Finally we need to swap [c] with [b]: + // fgha[c]|[b]de -> fgha[b]|[c]de. Because these two blocks are the same size, we are done. + // The Dolphin algorithm is attractive because it does the fewest array reads and writes: each + // array slot is read and written exactly once. However, it can have very poor memory locality: + // benchmarking shows it can take 7 times longer than the other two in some cases. The other two + // do n swaps, minus a delta (0 or 2 for Reversal, gcd(d, n) for Successive), so that's about + // twice as many reads and writes. But benchmarking shows that they usually perform better than + // Dolphin. Reversal is about as good as Successive on average, and it is much simpler, + // especially since we already have a `reverse` method. checkNotNull(array); checkPositionIndexes(fromIndex, toIndex, array.length); if (array.length <= 1) { diff --git a/android/guava/src/com/google/common/primitives/Longs.java b/android/guava/src/com/google/common/primitives/Longs.java index 6f60656bcb1a..656012f2ff3f 100644 --- a/android/guava/src/com/google/common/primitives/Longs.java +++ b/android/guava/src/com/google/common/primitives/Longs.java @@ -606,6 +606,56 @@ public static void reverse(long[] array, int fromIndex, int toIndex) { } } + /** + * Performs a right rotation of {@code array} of "distance" places, so that the first element is + * moved to index "distance", and the element at index {@code i} ends up at index {@code (distance + * + i) mod array.length}. This is equivalent to {@code Collections.rotate(Longs.asList(array), + * distance)}, but is considerably faster and avoids allocation and garbage collection. + * + *

The provided "distance" may be negative, which will rotate left. + * + * @since NEXT + */ + public static void rotate(long[] array, int distance) { + rotate(array, distance, 0, array.length); + } + + /** + * Performs a right rotation of {@code array} between {@code fromIndex} inclusive and {@code + * toIndex} exclusive. This is equivalent to {@code + * Collections.rotate(Longs.asList(array).subList(fromIndex, toIndex), distance)}, but is + * considerably faster and avoids allocations and garbage collection. + * + *

The provided "distance" may be negative, which will rotate left. + * + * @throws IndexOutOfBoundsException if {@code fromIndex < 0}, {@code toIndex > array.length}, or + * {@code toIndex > fromIndex} + * @since NEXT + */ + public static void rotate(long[] array, int distance, int fromIndex, int toIndex) { + // See Ints.rotate for more details about possible algorithms here. + checkNotNull(array); + checkPositionIndexes(fromIndex, toIndex, array.length); + if (array.length <= 1) { + return; + } + + int length = toIndex - fromIndex; + // Obtain m = (-distance mod length), a non-negative value less than "length". This is how many + // places left to rotate. + int m = -distance % length; + m = (m < 0) ? m + length : m; + // The current index of what will become the first element of the rotated section. + int newFirstIndex = m + fromIndex; + if (newFirstIndex == fromIndex) { + return; + } + + reverse(array, fromIndex, newFirstIndex); + reverse(array, newFirstIndex, toIndex); + reverse(array, fromIndex, toIndex); + } + /** * Returns an array containing each value of {@code collection}, converted to a {@code long} value * in the manner of {@link Number#longValue}. diff --git a/android/guava/src/com/google/common/primitives/Shorts.java b/android/guava/src/com/google/common/primitives/Shorts.java index 09e0f7cfc312..a53108672134 100644 --- a/android/guava/src/com/google/common/primitives/Shorts.java +++ b/android/guava/src/com/google/common/primitives/Shorts.java @@ -512,6 +512,56 @@ public static void reverse(short[] array, int fromIndex, int toIndex) { } } + /** + * Performs a right rotation of {@code array} of "distance" places, so that the first element is + * moved to index "distance", and the element at index {@code i} ends up at index {@code (distance + * + i) mod array.length}. This is equivalent to {@code Collections.rotate(Shorts.asList(array), + * distance)}, but is considerably faster and avoids allocation and garbage collection. + * + *

The provided "distance" may be negative, which will rotate left. + * + * @since NEXT + */ + public static void rotate(short[] array, int distance) { + rotate(array, distance, 0, array.length); + } + + /** + * Performs a right rotation of {@code array} between {@code fromIndex} inclusive and {@code + * toIndex} exclusive. This is equivalent to {@code + * Collections.rotate(Shorts.asList(array).subList(fromIndex, toIndex), distance)}, but is + * considerably faster and avoids allocations and garbage collection. + * + *

The provided "distance" may be negative, which will rotate left. + * + * @throws IndexOutOfBoundsException if {@code fromIndex < 0}, {@code toIndex > array.length}, or + * {@code toIndex > fromIndex} + * @since NEXT + */ + public static void rotate(short[] array, int distance, int fromIndex, int toIndex) { + // See Ints.rotate for more details about possible algorithms here. + checkNotNull(array); + checkPositionIndexes(fromIndex, toIndex, array.length); + if (array.length <= 1) { + return; + } + + int length = toIndex - fromIndex; + // Obtain m = (-distance mod length), a non-negative value less than "length". This is how many + // places left to rotate. + int m = -distance % length; + m = (m < 0) ? m + length : m; + // The current index of what will become the first element of the rotated section. + int newFirstIndex = m + fromIndex; + if (newFirstIndex == fromIndex) { + return; + } + + reverse(array, fromIndex, newFirstIndex); + reverse(array, newFirstIndex, toIndex); + reverse(array, fromIndex, toIndex); + } + /** * Returns an array containing each value of {@code collection}, converted to a {@code short} * value in the manner of {@link Number#shortValue}. diff --git a/guava-tests/test/com/google/common/cache/LocalLoadingCacheTest.java b/guava-tests/test/com/google/common/cache/LocalLoadingCacheTest.java index 06c007a9266e..6b73bdc422fc 100644 --- a/guava-tests/test/com/google/common/cache/LocalLoadingCacheTest.java +++ b/guava-tests/test/com/google/common/cache/LocalLoadingCacheTest.java @@ -157,7 +157,7 @@ public void testStatsNoops() { assertThat(map).containsEntry(three, one); assertThat(map).containsEntry(one, two); - // TODO(user): Confirm with fry@ that this is a reasonable substitute. + // TODO(cgruber): Confirm with fry@ that this is a reasonable substitute. // Set> entries = map.entrySet(); // assertThat(entries).containsExactly( // Maps.immutableEntry(three, one), Maps.immutableEntry(one, two)); diff --git a/guava-tests/test/com/google/common/primitives/BooleansTest.java b/guava-tests/test/com/google/common/primitives/BooleansTest.java index e91b19631754..c4367fa5168a 100644 --- a/guava-tests/test/com/google/common/primitives/BooleansTest.java +++ b/guava-tests/test/com/google/common/primitives/BooleansTest.java @@ -209,6 +209,237 @@ public void testReverseIndexed() { new boolean[] {true, true, false, false}, 1, 3, new boolean[] {true, false, true, false}); } + private static void testRotate(boolean[] input, int distance, boolean[] expectedOutput) { + input = Arrays.copyOf(input, input.length); + Booleans.rotate(input, distance); + assertThat(input).isEqualTo(expectedOutput); + } + + private static void testRotate( + boolean[] input, int distance, int fromIndex, int toIndex, boolean[] expectedOutput) { + input = Arrays.copyOf(input, input.length); + Booleans.rotate(input, distance, fromIndex, toIndex); + assertThat(input).isEqualTo(expectedOutput); + } + + public void testRotate() { + testRotate(new boolean[] {}, -1, new boolean[] {}); + testRotate(new boolean[] {}, 0, new boolean[] {}); + testRotate(new boolean[] {}, 1, new boolean[] {}); + + testRotate(new boolean[] {true}, -2, new boolean[] {true}); + testRotate(new boolean[] {true}, -1, new boolean[] {true}); + testRotate(new boolean[] {true}, 0, new boolean[] {true}); + testRotate(new boolean[] {true}, 1, new boolean[] {true}); + testRotate(new boolean[] {true}, 2, new boolean[] {true}); + + testRotate(new boolean[] {true, false}, -3, new boolean[] {false, true}); + testRotate(new boolean[] {true, false}, -1, new boolean[] {false, true}); + testRotate(new boolean[] {true, false}, -2, new boolean[] {true, false}); + testRotate(new boolean[] {true, false}, 0, new boolean[] {true, false}); + testRotate(new boolean[] {true, false}, 1, new boolean[] {false, true}); + testRotate(new boolean[] {true, false}, 2, new boolean[] {true, false}); + testRotate(new boolean[] {true, false}, 3, new boolean[] {false, true}); + + testRotate(new boolean[] {true, false, true}, -5, new boolean[] {true, true, false}); + testRotate(new boolean[] {true, false, true}, -4, new boolean[] {false, true, true}); + testRotate(new boolean[] {true, false, true}, -3, new boolean[] {true, false, true}); + testRotate(new boolean[] {true, false, true}, -2, new boolean[] {true, true, false}); + testRotate(new boolean[] {true, false, true}, -1, new boolean[] {false, true, true}); + testRotate(new boolean[] {true, false, true}, 0, new boolean[] {true, false, true}); + testRotate(new boolean[] {true, false, true}, 1, new boolean[] {true, true, false}); + testRotate(new boolean[] {true, false, true}, 2, new boolean[] {false, true, true}); + testRotate(new boolean[] {true, false, true}, 3, new boolean[] {true, false, true}); + testRotate(new boolean[] {true, false, true}, 4, new boolean[] {true, true, false}); + testRotate(new boolean[] {true, false, true}, 5, new boolean[] {false, true, true}); + + testRotate( + new boolean[] {true, false, true, false}, -9, new boolean[] {false, true, false, true}); + testRotate( + new boolean[] {true, false, true, false}, -5, new boolean[] {false, true, false, true}); + testRotate( + new boolean[] {true, false, true, false}, -1, new boolean[] {false, true, false, true}); + testRotate( + new boolean[] {true, false, true, false}, 0, new boolean[] {true, false, true, false}); + testRotate( + new boolean[] {true, false, true, false}, 1, new boolean[] {false, true, false, true}); + testRotate( + new boolean[] {true, false, true, false}, 5, new boolean[] {false, true, false, true}); + testRotate( + new boolean[] {true, false, true, false}, 9, new boolean[] {false, true, false, true}); + + testRotate( + new boolean[] {true, false, true, false, true}, + -6, + new boolean[] {false, true, false, true, true}); + testRotate( + new boolean[] {true, false, true, false, true}, + -4, + new boolean[] {true, true, false, true, false}); + testRotate( + new boolean[] {true, false, true, false, true}, + -3, + new boolean[] {false, true, true, false, true}); + testRotate( + new boolean[] {true, false, true, false, true}, + -1, + new boolean[] {false, true, false, true, true}); + testRotate( + new boolean[] {true, false, true, false, true}, + 0, + new boolean[] {true, false, true, false, true}); + testRotate( + new boolean[] {true, false, true, false, true}, + 1, + new boolean[] {true, true, false, true, false}); + testRotate( + new boolean[] {true, false, true, false, true}, + 3, + new boolean[] {true, false, true, true, false}); + testRotate( + new boolean[] {true, false, true, false, true}, + 4, + new boolean[] {false, true, false, true, true}); + testRotate( + new boolean[] {true, false, true, false, true}, + 6, + new boolean[] {true, true, false, true, false}); + } + + public void testRotateIndexed() { + testRotate(new boolean[] {}, 0, 0, 0, new boolean[] {}); + + testRotate(new boolean[] {true}, 0, 0, 1, new boolean[] {true}); + testRotate(new boolean[] {true}, 1, 0, 1, new boolean[] {true}); + testRotate(new boolean[] {true}, 1, 1, 1, new boolean[] {true}); + + // Rotate the central 5 elements, leaving the ends as-is + testRotate( + new boolean[] {false, true, false, true, false, true, false}, + -6, + 1, + 6, + new boolean[] {false, false, true, false, true, true, false}); + testRotate( + new boolean[] {false, true, false, true, false, true, false}, + -1, + 1, + 6, + new boolean[] {false, false, true, false, true, true, false}); + testRotate( + new boolean[] {false, true, false, true, false, true, false}, + 0, + 1, + 6, + new boolean[] {false, true, false, true, false, true, false}); + testRotate( + new boolean[] {false, true, false, true, false, true, false}, + 5, + 1, + 6, + new boolean[] {false, true, false, true, false, true, false}); + testRotate( + new boolean[] {false, true, false, true, false, true, false}, + 14, + 1, + 6, + new boolean[] {false, false, true, false, true, true, false}); + + // Rotate the first three elements + testRotate( + new boolean[] {false, true, false, true, false, true, false}, + -2, + 0, + 3, + new boolean[] {false, false, true, true, false, true, false}); + testRotate( + new boolean[] {false, true, false, true, false, true, false}, + -1, + 0, + 3, + new boolean[] {true, false, false, true, false, true, false}); + testRotate( + new boolean[] {false, true, false, true, false, true, false}, + 0, + 0, + 3, + new boolean[] {false, true, false, true, false, true, false}); + testRotate( + new boolean[] {false, true, false, true, false, true, false}, + 1, + 0, + 3, + new boolean[] {false, false, true, true, false, true, false}); + testRotate( + new boolean[] {false, true, false, true, false, true, false}, + 2, + 0, + 3, + new boolean[] {true, false, false, true, false, true, false}); + + // Rotate the last four elements + testRotate( + new boolean[] {false, true, false, true, false, true, false}, + -6, + 3, + 7, + new boolean[] {false, true, false, true, false, true, false}); + testRotate( + new boolean[] {false, true, false, true, false, true, false}, + -5, + 3, + 7, + new boolean[] {false, true, false, false, true, false, true}); + testRotate( + new boolean[] {false, true, false, true, false, true, false}, + -4, + 3, + 7, + new boolean[] {false, true, false, true, false, true, false}); + testRotate( + new boolean[] {false, true, false, true, false, true, false}, + -3, + 3, + 7, + new boolean[] {false, true, false, false, true, false, true}); + testRotate( + new boolean[] {false, true, false, true, false, true, false}, + -2, + 3, + 7, + new boolean[] {false, true, false, true, false, true, false}); + testRotate( + new boolean[] {false, true, false, true, false, true, false}, + -1, + 3, + 7, + new boolean[] {false, true, false, false, true, false, true}); + testRotate( + new boolean[] {false, true, false, true, false, true, false}, + 0, + 3, + 7, + new boolean[] {false, true, false, true, false, true, false}); + testRotate( + new boolean[] {false, true, false, true, false, true, false}, + 1, + 3, + 7, + new boolean[] {false, true, false, false, true, false, true}); + testRotate( + new boolean[] {false, true, false, true, false, true, false}, + 2, + 3, + 7, + new boolean[] {false, true, false, true, false, true, false}); + testRotate( + new boolean[] {false, true, false, true, false, true, false}, + 3, + 3, + 7, + new boolean[] {false, true, false, false, true, false, true}); + } + public void testToArray() { // need explicit type parameter to avoid javac warning!? List none = Arrays.asList(); diff --git a/guava-tests/test/com/google/common/primitives/BytesTest.java b/guava-tests/test/com/google/common/primitives/BytesTest.java index a1716bcc793d..da06a34bb95d 100644 --- a/guava-tests/test/com/google/common/primitives/BytesTest.java +++ b/guava-tests/test/com/google/common/primitives/BytesTest.java @@ -270,6 +270,103 @@ public void testReverseIndexed() { testReverse(new byte[] {-1, 1, -2, 2}, 1, 3, new byte[] {-1, -2, 1, 2}); } + private static void testRotate(byte[] input, int distance, byte[] expectedOutput) { + input = Arrays.copyOf(input, input.length); + Bytes.rotate(input, distance); + assertThat(input).isEqualTo(expectedOutput); + } + + private static void testRotate( + byte[] input, int distance, int fromIndex, int toIndex, byte[] expectedOutput) { + input = Arrays.copyOf(input, input.length); + Bytes.rotate(input, distance, fromIndex, toIndex); + assertThat(input).isEqualTo(expectedOutput); + } + + public void testRotate() { + testRotate(new byte[] {}, -1, new byte[] {}); + testRotate(new byte[] {}, 0, new byte[] {}); + testRotate(new byte[] {}, 1, new byte[] {}); + + testRotate(new byte[] {1}, -2, new byte[] {1}); + testRotate(new byte[] {1}, -1, new byte[] {1}); + testRotate(new byte[] {1}, 0, new byte[] {1}); + testRotate(new byte[] {1}, 1, new byte[] {1}); + testRotate(new byte[] {1}, 2, new byte[] {1}); + + testRotate(new byte[] {1, 2}, -3, new byte[] {2, 1}); + testRotate(new byte[] {1, 2}, -1, new byte[] {2, 1}); + testRotate(new byte[] {1, 2}, -2, new byte[] {1, 2}); + testRotate(new byte[] {1, 2}, 0, new byte[] {1, 2}); + testRotate(new byte[] {1, 2}, 1, new byte[] {2, 1}); + testRotate(new byte[] {1, 2}, 2, new byte[] {1, 2}); + testRotate(new byte[] {1, 2}, 3, new byte[] {2, 1}); + + testRotate(new byte[] {1, 2, 3}, -5, new byte[] {3, 1, 2}); + testRotate(new byte[] {1, 2, 3}, -4, new byte[] {2, 3, 1}); + testRotate(new byte[] {1, 2, 3}, -3, new byte[] {1, 2, 3}); + testRotate(new byte[] {1, 2, 3}, -2, new byte[] {3, 1, 2}); + testRotate(new byte[] {1, 2, 3}, -1, new byte[] {2, 3, 1}); + testRotate(new byte[] {1, 2, 3}, 0, new byte[] {1, 2, 3}); + testRotate(new byte[] {1, 2, 3}, 1, new byte[] {3, 1, 2}); + testRotate(new byte[] {1, 2, 3}, 2, new byte[] {2, 3, 1}); + testRotate(new byte[] {1, 2, 3}, 3, new byte[] {1, 2, 3}); + testRotate(new byte[] {1, 2, 3}, 4, new byte[] {3, 1, 2}); + testRotate(new byte[] {1, 2, 3}, 5, new byte[] {2, 3, 1}); + + testRotate(new byte[] {1, 2, 3, 4}, -9, new byte[] {2, 3, 4, 1}); + testRotate(new byte[] {1, 2, 3, 4}, -5, new byte[] {2, 3, 4, 1}); + testRotate(new byte[] {1, 2, 3, 4}, -1, new byte[] {2, 3, 4, 1}); + testRotate(new byte[] {1, 2, 3, 4}, 0, new byte[] {1, 2, 3, 4}); + testRotate(new byte[] {1, 2, 3, 4}, 1, new byte[] {4, 1, 2, 3}); + testRotate(new byte[] {1, 2, 3, 4}, 5, new byte[] {4, 1, 2, 3}); + testRotate(new byte[] {1, 2, 3, 4}, 9, new byte[] {4, 1, 2, 3}); + + testRotate(new byte[] {1, 2, 3, 4, 5}, -6, new byte[] {2, 3, 4, 5, 1}); + testRotate(new byte[] {1, 2, 3, 4, 5}, -4, new byte[] {5, 1, 2, 3, 4}); + testRotate(new byte[] {1, 2, 3, 4, 5}, -3, new byte[] {4, 5, 1, 2, 3}); + testRotate(new byte[] {1, 2, 3, 4, 5}, -1, new byte[] {2, 3, 4, 5, 1}); + testRotate(new byte[] {1, 2, 3, 4, 5}, 0, new byte[] {1, 2, 3, 4, 5}); + testRotate(new byte[] {1, 2, 3, 4, 5}, 1, new byte[] {5, 1, 2, 3, 4}); + testRotate(new byte[] {1, 2, 3, 4, 5}, 3, new byte[] {3, 4, 5, 1, 2}); + testRotate(new byte[] {1, 2, 3, 4, 5}, 4, new byte[] {2, 3, 4, 5, 1}); + testRotate(new byte[] {1, 2, 3, 4, 5}, 6, new byte[] {5, 1, 2, 3, 4}); + } + + public void testRotateIndexed() { + testRotate(new byte[] {}, 0, 0, 0, new byte[] {}); + + testRotate(new byte[] {1}, 0, 0, 1, new byte[] {1}); + testRotate(new byte[] {1}, 1, 0, 1, new byte[] {1}); + testRotate(new byte[] {1}, 1, 1, 1, new byte[] {1}); + + // Rotate the central 5 elements, leaving the ends as-is + testRotate(new byte[] {0, 1, 2, 3, 4, 5, 6}, -6, 1, 6, new byte[] {0, 2, 3, 4, 5, 1, 6}); + testRotate(new byte[] {0, 1, 2, 3, 4, 5, 6}, -1, 1, 6, new byte[] {0, 2, 3, 4, 5, 1, 6}); + testRotate(new byte[] {0, 1, 2, 3, 4, 5, 6}, 0, 1, 6, new byte[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new byte[] {0, 1, 2, 3, 4, 5, 6}, 5, 1, 6, new byte[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new byte[] {0, 1, 2, 3, 4, 5, 6}, 14, 1, 6, new byte[] {0, 2, 3, 4, 5, 1, 6}); + + // Rotate the first three elements + testRotate(new byte[] {0, 1, 2, 3, 4, 5, 6}, -2, 0, 3, new byte[] {2, 0, 1, 3, 4, 5, 6}); + testRotate(new byte[] {0, 1, 2, 3, 4, 5, 6}, -1, 0, 3, new byte[] {1, 2, 0, 3, 4, 5, 6}); + testRotate(new byte[] {0, 1, 2, 3, 4, 5, 6}, 0, 0, 3, new byte[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new byte[] {0, 1, 2, 3, 4, 5, 6}, 1, 0, 3, new byte[] {2, 0, 1, 3, 4, 5, 6}); + testRotate(new byte[] {0, 1, 2, 3, 4, 5, 6}, 2, 0, 3, new byte[] {1, 2, 0, 3, 4, 5, 6}); + + // Rotate the last four elements + testRotate(new byte[] {0, 1, 2, 3, 4, 5, 6}, -6, 3, 7, new byte[] {0, 1, 2, 5, 6, 3, 4}); + testRotate(new byte[] {0, 1, 2, 3, 4, 5, 6}, -5, 3, 7, new byte[] {0, 1, 2, 4, 5, 6, 3}); + testRotate(new byte[] {0, 1, 2, 3, 4, 5, 6}, -4, 3, 7, new byte[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new byte[] {0, 1, 2, 3, 4, 5, 6}, -3, 3, 7, new byte[] {0, 1, 2, 6, 3, 4, 5}); + testRotate(new byte[] {0, 1, 2, 3, 4, 5, 6}, -2, 3, 7, new byte[] {0, 1, 2, 5, 6, 3, 4}); + testRotate(new byte[] {0, 1, 2, 3, 4, 5, 6}, -1, 3, 7, new byte[] {0, 1, 2, 4, 5, 6, 3}); + testRotate(new byte[] {0, 1, 2, 3, 4, 5, 6}, 0, 3, 7, new byte[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new byte[] {0, 1, 2, 3, 4, 5, 6}, 1, 3, 7, new byte[] {0, 1, 2, 6, 3, 4, 5}); + testRotate(new byte[] {0, 1, 2, 3, 4, 5, 6}, 2, 3, 7, new byte[] {0, 1, 2, 5, 6, 3, 4}); + testRotate(new byte[] {0, 1, 2, 3, 4, 5, 6}, 3, 3, 7, new byte[] {0, 1, 2, 4, 5, 6, 3}); + } + @GwtIncompatible // NullPointerTester public void testNulls() { new NullPointerTester().testAllPublicStaticMethods(Bytes.class); diff --git a/guava-tests/test/com/google/common/primitives/CharsTest.java b/guava-tests/test/com/google/common/primitives/CharsTest.java index 099e02443b15..3f1d4156e169 100644 --- a/guava-tests/test/com/google/common/primitives/CharsTest.java +++ b/guava-tests/test/com/google/common/primitives/CharsTest.java @@ -359,6 +359,203 @@ public void testReverseIndexed() { testReverse(new char[] {'A', '1', 'B', '2'}, 1, 3, new char[] {'A', 'B', '1', '2'}); } + private static void testRotate(char[] input, int distance, char[] expectedOutput) { + input = Arrays.copyOf(input, input.length); + Chars.rotate(input, distance); + assertThat(input).isEqualTo(expectedOutput); + } + + private static void testRotate( + char[] input, int distance, int fromIndex, int toIndex, char[] expectedOutput) { + input = Arrays.copyOf(input, input.length); + Chars.rotate(input, distance, fromIndex, toIndex); + assertThat(input).isEqualTo(expectedOutput); + } + + public void testRotate() { + testRotate(new char[] {}, -1, new char[] {}); + testRotate(new char[] {}, 0, new char[] {}); + testRotate(new char[] {}, 1, new char[] {}); + + testRotate(new char[] {'1'}, -2, new char[] {'1'}); + testRotate(new char[] {'1'}, -1, new char[] {'1'}); + testRotate(new char[] {'1'}, 0, new char[] {'1'}); + testRotate(new char[] {'1'}, 1, new char[] {'1'}); + testRotate(new char[] {'1'}, 2, new char[] {'1'}); + + testRotate(new char[] {'1', '2'}, -3, new char[] {'2', '1'}); + testRotate(new char[] {'1', '2'}, -1, new char[] {'2', '1'}); + testRotate(new char[] {'1', '2'}, -2, new char[] {'1', '2'}); + testRotate(new char[] {'1', '2'}, 0, new char[] {'1', '2'}); + testRotate(new char[] {'1', '2'}, 1, new char[] {'2', '1'}); + testRotate(new char[] {'1', '2'}, 2, new char[] {'1', '2'}); + testRotate(new char[] {'1', '2'}, 3, new char[] {'2', '1'}); + + testRotate(new char[] {'1', '2', '3'}, -5, new char[] {'3', '1', '2'}); + testRotate(new char[] {'1', '2', '3'}, -4, new char[] {'2', '3', '1'}); + testRotate(new char[] {'1', '2', '3'}, -3, new char[] {'1', '2', '3'}); + testRotate(new char[] {'1', '2', '3'}, -2, new char[] {'3', '1', '2'}); + testRotate(new char[] {'1', '2', '3'}, -1, new char[] {'2', '3', '1'}); + testRotate(new char[] {'1', '2', '3'}, 0, new char[] {'1', '2', '3'}); + testRotate(new char[] {'1', '2', '3'}, 1, new char[] {'3', '1', '2'}); + testRotate(new char[] {'1', '2', '3'}, 2, new char[] {'2', '3', '1'}); + testRotate(new char[] {'1', '2', '3'}, 3, new char[] {'1', '2', '3'}); + testRotate(new char[] {'1', '2', '3'}, 4, new char[] {'3', '1', '2'}); + testRotate(new char[] {'1', '2', '3'}, 5, new char[] {'2', '3', '1'}); + + testRotate(new char[] {'1', '2', '3', '4'}, -9, new char[] {'2', '3', '4', '1'}); + testRotate(new char[] {'1', '2', '3', '4'}, -5, new char[] {'2', '3', '4', '1'}); + testRotate(new char[] {'1', '2', '3', '4'}, -1, new char[] {'2', '3', '4', '1'}); + testRotate(new char[] {'1', '2', '3', '4'}, 0, new char[] {'1', '2', '3', '4'}); + testRotate(new char[] {'1', '2', '3', '4'}, 1, new char[] {'4', '1', '2', '3'}); + testRotate(new char[] {'1', '2', '3', '4'}, 5, new char[] {'4', '1', '2', '3'}); + testRotate(new char[] {'1', '2', '3', '4'}, 9, new char[] {'4', '1', '2', '3'}); + + testRotate(new char[] {'1', '2', '3', '4', '5'}, -6, new char[] {'2', '3', '4', '5', '1'}); + testRotate(new char[] {'1', '2', '3', '4', '5'}, -4, new char[] {'5', '1', '2', '3', '4'}); + testRotate(new char[] {'1', '2', '3', '4', '5'}, -3, new char[] {'4', '5', '1', '2', '3'}); + testRotate(new char[] {'1', '2', '3', '4', '5'}, -1, new char[] {'2', '3', '4', '5', '1'}); + testRotate(new char[] {'1', '2', '3', '4', '5'}, 0, new char[] {'1', '2', '3', '4', '5'}); + testRotate(new char[] {'1', '2', '3', '4', '5'}, 1, new char[] {'5', '1', '2', '3', '4'}); + testRotate(new char[] {'1', '2', '3', '4', '5'}, 3, new char[] {'3', '4', '5', '1', '2'}); + testRotate(new char[] {'1', '2', '3', '4', '5'}, 4, new char[] {'2', '3', '4', '5', '1'}); + testRotate(new char[] {'1', '2', '3', '4', '5'}, 6, new char[] {'5', '1', '2', '3', '4'}); + } + + public void testRotateIndexed() { + testRotate(new char[] {}, 0, 0, 0, new char[] {}); + + testRotate(new char[] {'1'}, 0, 0, 1, new char[] {'1'}); + testRotate(new char[] {'1'}, 1, 0, 1, new char[] {'1'}); + testRotate(new char[] {'1'}, 1, 1, 1, new char[] {'1'}); + + // Rotate the central 5 elements, leaving the ends as-is + testRotate( + new char[] {'0', '1', '2', '3', '4', '5', '6'}, + -6, + 1, + 6, + new char[] {'0', '2', '3', '4', '5', '1', '6'}); + testRotate( + new char[] {'0', '1', '2', '3', '4', '5', '6'}, + -1, + 1, + 6, + new char[] {'0', '2', '3', '4', '5', '1', '6'}); + testRotate( + new char[] {'0', '1', '2', '3', '4', '5', '6'}, + 0, + 1, + 6, + new char[] {'0', '1', '2', '3', '4', '5', '6'}); + testRotate( + new char[] {'0', '1', '2', '3', '4', '5', '6'}, + 5, + 1, + 6, + new char[] {'0', '1', '2', '3', '4', '5', '6'}); + testRotate( + new char[] {'0', '1', '2', '3', '4', '5', '6'}, + 14, + 1, + 6, + new char[] {'0', '2', '3', '4', '5', '1', '6'}); + + // Rotate the first three elements + testRotate( + new char[] {'0', '1', '2', '3', '4', '5', '6'}, + -2, + 0, + 3, + new char[] {'2', '0', '1', '3', '4', '5', '6'}); + testRotate( + new char[] {'0', '1', '2', '3', '4', '5', '6'}, + -1, + 0, + 3, + new char[] {'1', '2', '0', '3', '4', '5', '6'}); + testRotate( + new char[] {'0', '1', '2', '3', '4', '5', '6'}, + 0, + 0, + 3, + new char[] {'0', '1', '2', '3', '4', '5', '6'}); + testRotate( + new char[] {'0', '1', '2', '3', '4', '5', '6'}, + 1, + 0, + 3, + new char[] {'2', '0', '1', '3', '4', '5', '6'}); + testRotate( + new char[] {'0', '1', '2', '3', '4', '5', '6'}, + 2, + 0, + 3, + new char[] {'1', '2', '0', '3', '4', '5', '6'}); + + // Rotate the last four elements + testRotate( + new char[] {'0', '1', '2', '3', '4', '5', '6'}, + -6, + 3, + 7, + new char[] {'0', '1', '2', '5', '6', '3', '4'}); + testRotate( + new char[] {'0', '1', '2', '3', '4', '5', '6'}, + -5, + 3, + 7, + new char[] {'0', '1', '2', '4', '5', '6', '3'}); + testRotate( + new char[] {'0', '1', '2', '3', '4', '5', '6'}, + -4, + 3, + 7, + new char[] {'0', '1', '2', '3', '4', '5', '6'}); + testRotate( + new char[] {'0', '1', '2', '3', '4', '5', '6'}, + -3, + 3, + 7, + new char[] {'0', '1', '2', '6', '3', '4', '5'}); + testRotate( + new char[] {'0', '1', '2', '3', '4', '5', '6'}, + -2, + 3, + 7, + new char[] {'0', '1', '2', '5', '6', '3', '4'}); + testRotate( + new char[] {'0', '1', '2', '3', '4', '5', '6'}, + -1, + 3, + 7, + new char[] {'0', '1', '2', '4', '5', '6', '3'}); + testRotate( + new char[] {'0', '1', '2', '3', '4', '5', '6'}, + 0, + 3, + 7, + new char[] {'0', '1', '2', '3', '4', '5', '6'}); + testRotate( + new char[] {'0', '1', '2', '3', '4', '5', '6'}, + 1, + 3, + 7, + new char[] {'0', '1', '2', '6', '3', '4', '5'}); + testRotate( + new char[] {'0', '1', '2', '3', '4', '5', '6'}, + 2, + 3, + 7, + new char[] {'0', '1', '2', '5', '6', '3', '4'}); + testRotate( + new char[] {'0', '1', '2', '3', '4', '5', '6'}, + 3, + 3, + 7, + new char[] {'0', '1', '2', '4', '5', '6', '3'}); + } + public void testSortDescending() { testSortDescending(new char[] {}, new char[] {}); testSortDescending(new char[] {'1'}, new char[] {'1'}); diff --git a/guava-tests/test/com/google/common/primitives/DoublesTest.java b/guava-tests/test/com/google/common/primitives/DoublesTest.java index 930c2e420a57..f4860f5601e0 100644 --- a/guava-tests/test/com/google/common/primitives/DoublesTest.java +++ b/guava-tests/test/com/google/common/primitives/DoublesTest.java @@ -365,6 +365,103 @@ public void testReverseIndexed() { testReverse(new double[] {-1, 1, -2, 2}, 1, 3, new double[] {-1, -2, 1, 2}); } + private static void testRotate(double[] input, int distance, double[] expectedOutput) { + input = Arrays.copyOf(input, input.length); + Doubles.rotate(input, distance); + assertThat(input).isEqualTo(expectedOutput); + } + + private static void testRotate( + double[] input, int distance, int fromIndex, int toIndex, double[] expectedOutput) { + input = Arrays.copyOf(input, input.length); + Doubles.rotate(input, distance, fromIndex, toIndex); + assertThat(input).isEqualTo(expectedOutput); + } + + public void testRotate() { + testRotate(new double[] {}, -1, new double[] {}); + testRotate(new double[] {}, 0, new double[] {}); + testRotate(new double[] {}, 1, new double[] {}); + + testRotate(new double[] {1}, -2, new double[] {1}); + testRotate(new double[] {1}, -1, new double[] {1}); + testRotate(new double[] {1}, 0, new double[] {1}); + testRotate(new double[] {1}, 1, new double[] {1}); + testRotate(new double[] {1}, 2, new double[] {1}); + + testRotate(new double[] {1, 2}, -3, new double[] {2, 1}); + testRotate(new double[] {1, 2}, -1, new double[] {2, 1}); + testRotate(new double[] {1, 2}, -2, new double[] {1, 2}); + testRotate(new double[] {1, 2}, 0, new double[] {1, 2}); + testRotate(new double[] {1, 2}, 1, new double[] {2, 1}); + testRotate(new double[] {1, 2}, 2, new double[] {1, 2}); + testRotate(new double[] {1, 2}, 3, new double[] {2, 1}); + + testRotate(new double[] {1, 2, 3}, -5, new double[] {3, 1, 2}); + testRotate(new double[] {1, 2, 3}, -4, new double[] {2, 3, 1}); + testRotate(new double[] {1, 2, 3}, -3, new double[] {1, 2, 3}); + testRotate(new double[] {1, 2, 3}, -2, new double[] {3, 1, 2}); + testRotate(new double[] {1, 2, 3}, -1, new double[] {2, 3, 1}); + testRotate(new double[] {1, 2, 3}, 0, new double[] {1, 2, 3}); + testRotate(new double[] {1, 2, 3}, 1, new double[] {3, 1, 2}); + testRotate(new double[] {1, 2, 3}, 2, new double[] {2, 3, 1}); + testRotate(new double[] {1, 2, 3}, 3, new double[] {1, 2, 3}); + testRotate(new double[] {1, 2, 3}, 4, new double[] {3, 1, 2}); + testRotate(new double[] {1, 2, 3}, 5, new double[] {2, 3, 1}); + + testRotate(new double[] {1, 2, 3, 4}, -9, new double[] {2, 3, 4, 1}); + testRotate(new double[] {1, 2, 3, 4}, -5, new double[] {2, 3, 4, 1}); + testRotate(new double[] {1, 2, 3, 4}, -1, new double[] {2, 3, 4, 1}); + testRotate(new double[] {1, 2, 3, 4}, 0, new double[] {1, 2, 3, 4}); + testRotate(new double[] {1, 2, 3, 4}, 1, new double[] {4, 1, 2, 3}); + testRotate(new double[] {1, 2, 3, 4}, 5, new double[] {4, 1, 2, 3}); + testRotate(new double[] {1, 2, 3, 4}, 9, new double[] {4, 1, 2, 3}); + + testRotate(new double[] {1, 2, 3, 4, 5}, -6, new double[] {2, 3, 4, 5, 1}); + testRotate(new double[] {1, 2, 3, 4, 5}, -4, new double[] {5, 1, 2, 3, 4}); + testRotate(new double[] {1, 2, 3, 4, 5}, -3, new double[] {4, 5, 1, 2, 3}); + testRotate(new double[] {1, 2, 3, 4, 5}, -1, new double[] {2, 3, 4, 5, 1}); + testRotate(new double[] {1, 2, 3, 4, 5}, 0, new double[] {1, 2, 3, 4, 5}); + testRotate(new double[] {1, 2, 3, 4, 5}, 1, new double[] {5, 1, 2, 3, 4}); + testRotate(new double[] {1, 2, 3, 4, 5}, 3, new double[] {3, 4, 5, 1, 2}); + testRotate(new double[] {1, 2, 3, 4, 5}, 4, new double[] {2, 3, 4, 5, 1}); + testRotate(new double[] {1, 2, 3, 4, 5}, 6, new double[] {5, 1, 2, 3, 4}); + } + + public void testRotateIndexed() { + testRotate(new double[] {}, 0, 0, 0, new double[] {}); + + testRotate(new double[] {1}, 0, 0, 1, new double[] {1}); + testRotate(new double[] {1}, 1, 0, 1, new double[] {1}); + testRotate(new double[] {1}, 1, 1, 1, new double[] {1}); + + // Rotate the central 5 elements, leaving the ends as-is + testRotate(new double[] {0, 1, 2, 3, 4, 5, 6}, -6, 1, 6, new double[] {0, 2, 3, 4, 5, 1, 6}); + testRotate(new double[] {0, 1, 2, 3, 4, 5, 6}, -1, 1, 6, new double[] {0, 2, 3, 4, 5, 1, 6}); + testRotate(new double[] {0, 1, 2, 3, 4, 5, 6}, 0, 1, 6, new double[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new double[] {0, 1, 2, 3, 4, 5, 6}, 5, 1, 6, new double[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new double[] {0, 1, 2, 3, 4, 5, 6}, 14, 1, 6, new double[] {0, 2, 3, 4, 5, 1, 6}); + + // Rotate the first three elements + testRotate(new double[] {0, 1, 2, 3, 4, 5, 6}, -2, 0, 3, new double[] {2, 0, 1, 3, 4, 5, 6}); + testRotate(new double[] {0, 1, 2, 3, 4, 5, 6}, -1, 0, 3, new double[] {1, 2, 0, 3, 4, 5, 6}); + testRotate(new double[] {0, 1, 2, 3, 4, 5, 6}, 0, 0, 3, new double[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new double[] {0, 1, 2, 3, 4, 5, 6}, 1, 0, 3, new double[] {2, 0, 1, 3, 4, 5, 6}); + testRotate(new double[] {0, 1, 2, 3, 4, 5, 6}, 2, 0, 3, new double[] {1, 2, 0, 3, 4, 5, 6}); + + // Rotate the last four elements + testRotate(new double[] {0, 1, 2, 3, 4, 5, 6}, -6, 3, 7, new double[] {0, 1, 2, 5, 6, 3, 4}); + testRotate(new double[] {0, 1, 2, 3, 4, 5, 6}, -5, 3, 7, new double[] {0, 1, 2, 4, 5, 6, 3}); + testRotate(new double[] {0, 1, 2, 3, 4, 5, 6}, -4, 3, 7, new double[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new double[] {0, 1, 2, 3, 4, 5, 6}, -3, 3, 7, new double[] {0, 1, 2, 6, 3, 4, 5}); + testRotate(new double[] {0, 1, 2, 3, 4, 5, 6}, -2, 3, 7, new double[] {0, 1, 2, 5, 6, 3, 4}); + testRotate(new double[] {0, 1, 2, 3, 4, 5, 6}, -1, 3, 7, new double[] {0, 1, 2, 4, 5, 6, 3}); + testRotate(new double[] {0, 1, 2, 3, 4, 5, 6}, 0, 3, 7, new double[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new double[] {0, 1, 2, 3, 4, 5, 6}, 1, 3, 7, new double[] {0, 1, 2, 6, 3, 4, 5}); + testRotate(new double[] {0, 1, 2, 3, 4, 5, 6}, 2, 3, 7, new double[] {0, 1, 2, 5, 6, 3, 4}); + testRotate(new double[] {0, 1, 2, 3, 4, 5, 6}, 3, 3, 7, new double[] {0, 1, 2, 4, 5, 6, 3}); + } + public void testSortDescending() { testSortDescending(new double[] {}, new double[] {}); testSortDescending(new double[] {1}, new double[] {1}); diff --git a/guava-tests/test/com/google/common/primitives/FloatsTest.java b/guava-tests/test/com/google/common/primitives/FloatsTest.java index 1833f26e28c1..75d5e8bff304 100644 --- a/guava-tests/test/com/google/common/primitives/FloatsTest.java +++ b/guava-tests/test/com/google/common/primitives/FloatsTest.java @@ -350,6 +350,103 @@ public void testReverseIndexed() { testReverse(new float[] {-1, 1, -2, 2}, 1, 3, new float[] {-1, -2, 1, 2}); } + private static void testRotate(float[] input, int distance, float[] expectedOutput) { + input = Arrays.copyOf(input, input.length); + Floats.rotate(input, distance); + assertThat(input).isEqualTo(expectedOutput); + } + + private static void testRotate( + float[] input, int distance, int fromIndex, int toIndex, float[] expectedOutput) { + input = Arrays.copyOf(input, input.length); + Floats.rotate(input, distance, fromIndex, toIndex); + assertThat(input).isEqualTo(expectedOutput); + } + + public void testRotate() { + testRotate(new float[] {}, -1, new float[] {}); + testRotate(new float[] {}, 0, new float[] {}); + testRotate(new float[] {}, 1, new float[] {}); + + testRotate(new float[] {1}, -2, new float[] {1}); + testRotate(new float[] {1}, -1, new float[] {1}); + testRotate(new float[] {1}, 0, new float[] {1}); + testRotate(new float[] {1}, 1, new float[] {1}); + testRotate(new float[] {1}, 2, new float[] {1}); + + testRotate(new float[] {1, 2}, -3, new float[] {2, 1}); + testRotate(new float[] {1, 2}, -1, new float[] {2, 1}); + testRotate(new float[] {1, 2}, -2, new float[] {1, 2}); + testRotate(new float[] {1, 2}, 0, new float[] {1, 2}); + testRotate(new float[] {1, 2}, 1, new float[] {2, 1}); + testRotate(new float[] {1, 2}, 2, new float[] {1, 2}); + testRotate(new float[] {1, 2}, 3, new float[] {2, 1}); + + testRotate(new float[] {1, 2, 3}, -5, new float[] {3, 1, 2}); + testRotate(new float[] {1, 2, 3}, -4, new float[] {2, 3, 1}); + testRotate(new float[] {1, 2, 3}, -3, new float[] {1, 2, 3}); + testRotate(new float[] {1, 2, 3}, -2, new float[] {3, 1, 2}); + testRotate(new float[] {1, 2, 3}, -1, new float[] {2, 3, 1}); + testRotate(new float[] {1, 2, 3}, 0, new float[] {1, 2, 3}); + testRotate(new float[] {1, 2, 3}, 1, new float[] {3, 1, 2}); + testRotate(new float[] {1, 2, 3}, 2, new float[] {2, 3, 1}); + testRotate(new float[] {1, 2, 3}, 3, new float[] {1, 2, 3}); + testRotate(new float[] {1, 2, 3}, 4, new float[] {3, 1, 2}); + testRotate(new float[] {1, 2, 3}, 5, new float[] {2, 3, 1}); + + testRotate(new float[] {1, 2, 3, 4}, -9, new float[] {2, 3, 4, 1}); + testRotate(new float[] {1, 2, 3, 4}, -5, new float[] {2, 3, 4, 1}); + testRotate(new float[] {1, 2, 3, 4}, -1, new float[] {2, 3, 4, 1}); + testRotate(new float[] {1, 2, 3, 4}, 0, new float[] {1, 2, 3, 4}); + testRotate(new float[] {1, 2, 3, 4}, 1, new float[] {4, 1, 2, 3}); + testRotate(new float[] {1, 2, 3, 4}, 5, new float[] {4, 1, 2, 3}); + testRotate(new float[] {1, 2, 3, 4}, 9, new float[] {4, 1, 2, 3}); + + testRotate(new float[] {1, 2, 3, 4, 5}, -6, new float[] {2, 3, 4, 5, 1}); + testRotate(new float[] {1, 2, 3, 4, 5}, -4, new float[] {5, 1, 2, 3, 4}); + testRotate(new float[] {1, 2, 3, 4, 5}, -3, new float[] {4, 5, 1, 2, 3}); + testRotate(new float[] {1, 2, 3, 4, 5}, -1, new float[] {2, 3, 4, 5, 1}); + testRotate(new float[] {1, 2, 3, 4, 5}, 0, new float[] {1, 2, 3, 4, 5}); + testRotate(new float[] {1, 2, 3, 4, 5}, 1, new float[] {5, 1, 2, 3, 4}); + testRotate(new float[] {1, 2, 3, 4, 5}, 3, new float[] {3, 4, 5, 1, 2}); + testRotate(new float[] {1, 2, 3, 4, 5}, 4, new float[] {2, 3, 4, 5, 1}); + testRotate(new float[] {1, 2, 3, 4, 5}, 6, new float[] {5, 1, 2, 3, 4}); + } + + public void testRotateIndexed() { + testRotate(new float[] {}, 0, 0, 0, new float[] {}); + + testRotate(new float[] {1}, 0, 0, 1, new float[] {1}); + testRotate(new float[] {1}, 1, 0, 1, new float[] {1}); + testRotate(new float[] {1}, 1, 1, 1, new float[] {1}); + + // Rotate the central 5 elements, leaving the ends as-is + testRotate(new float[] {0, 1, 2, 3, 4, 5, 6}, -6, 1, 6, new float[] {0, 2, 3, 4, 5, 1, 6}); + testRotate(new float[] {0, 1, 2, 3, 4, 5, 6}, -1, 1, 6, new float[] {0, 2, 3, 4, 5, 1, 6}); + testRotate(new float[] {0, 1, 2, 3, 4, 5, 6}, 0, 1, 6, new float[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new float[] {0, 1, 2, 3, 4, 5, 6}, 5, 1, 6, new float[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new float[] {0, 1, 2, 3, 4, 5, 6}, 14, 1, 6, new float[] {0, 2, 3, 4, 5, 1, 6}); + + // Rotate the first three elements + testRotate(new float[] {0, 1, 2, 3, 4, 5, 6}, -2, 0, 3, new float[] {2, 0, 1, 3, 4, 5, 6}); + testRotate(new float[] {0, 1, 2, 3, 4, 5, 6}, -1, 0, 3, new float[] {1, 2, 0, 3, 4, 5, 6}); + testRotate(new float[] {0, 1, 2, 3, 4, 5, 6}, 0, 0, 3, new float[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new float[] {0, 1, 2, 3, 4, 5, 6}, 1, 0, 3, new float[] {2, 0, 1, 3, 4, 5, 6}); + testRotate(new float[] {0, 1, 2, 3, 4, 5, 6}, 2, 0, 3, new float[] {1, 2, 0, 3, 4, 5, 6}); + + // Rotate the last four elements + testRotate(new float[] {0, 1, 2, 3, 4, 5, 6}, -6, 3, 7, new float[] {0, 1, 2, 5, 6, 3, 4}); + testRotate(new float[] {0, 1, 2, 3, 4, 5, 6}, -5, 3, 7, new float[] {0, 1, 2, 4, 5, 6, 3}); + testRotate(new float[] {0, 1, 2, 3, 4, 5, 6}, -4, 3, 7, new float[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new float[] {0, 1, 2, 3, 4, 5, 6}, -3, 3, 7, new float[] {0, 1, 2, 6, 3, 4, 5}); + testRotate(new float[] {0, 1, 2, 3, 4, 5, 6}, -2, 3, 7, new float[] {0, 1, 2, 5, 6, 3, 4}); + testRotate(new float[] {0, 1, 2, 3, 4, 5, 6}, -1, 3, 7, new float[] {0, 1, 2, 4, 5, 6, 3}); + testRotate(new float[] {0, 1, 2, 3, 4, 5, 6}, 0, 3, 7, new float[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new float[] {0, 1, 2, 3, 4, 5, 6}, 1, 3, 7, new float[] {0, 1, 2, 6, 3, 4, 5}); + testRotate(new float[] {0, 1, 2, 3, 4, 5, 6}, 2, 3, 7, new float[] {0, 1, 2, 5, 6, 3, 4}); + testRotate(new float[] {0, 1, 2, 3, 4, 5, 6}, 3, 3, 7, new float[] {0, 1, 2, 4, 5, 6, 3}); + } + public void testSortDescending() { testSortDescending(new float[] {}, new float[] {}); testSortDescending(new float[] {1}, new float[] {1}); diff --git a/guava-tests/test/com/google/common/primitives/LongsTest.java b/guava-tests/test/com/google/common/primitives/LongsTest.java index 401dc2a43ebe..938df8336a6c 100644 --- a/guava-tests/test/com/google/common/primitives/LongsTest.java +++ b/guava-tests/test/com/google/common/primitives/LongsTest.java @@ -354,6 +354,103 @@ public void testReverseIndexed() { testReverse(new long[] {-1, 1, -2, 2}, 1, 3, new long[] {-1, -2, 1, 2}); } + private static void testRotate(long[] input, int distance, long[] expectedOutput) { + input = Arrays.copyOf(input, input.length); + Longs.rotate(input, distance); + assertThat(input).isEqualTo(expectedOutput); + } + + private static void testRotate( + long[] input, int distance, int fromIndex, int toIndex, long[] expectedOutput) { + input = Arrays.copyOf(input, input.length); + Longs.rotate(input, distance, fromIndex, toIndex); + assertThat(input).isEqualTo(expectedOutput); + } + + public void testRotate() { + testRotate(new long[] {}, -1, new long[] {}); + testRotate(new long[] {}, 0, new long[] {}); + testRotate(new long[] {}, 1, new long[] {}); + + testRotate(new long[] {1}, -2, new long[] {1}); + testRotate(new long[] {1}, -1, new long[] {1}); + testRotate(new long[] {1}, 0, new long[] {1}); + testRotate(new long[] {1}, 1, new long[] {1}); + testRotate(new long[] {1}, 2, new long[] {1}); + + testRotate(new long[] {1, 2}, -3, new long[] {2, 1}); + testRotate(new long[] {1, 2}, -1, new long[] {2, 1}); + testRotate(new long[] {1, 2}, -2, new long[] {1, 2}); + testRotate(new long[] {1, 2}, 0, new long[] {1, 2}); + testRotate(new long[] {1, 2}, 1, new long[] {2, 1}); + testRotate(new long[] {1, 2}, 2, new long[] {1, 2}); + testRotate(new long[] {1, 2}, 3, new long[] {2, 1}); + + testRotate(new long[] {1, 2, 3}, -5, new long[] {3, 1, 2}); + testRotate(new long[] {1, 2, 3}, -4, new long[] {2, 3, 1}); + testRotate(new long[] {1, 2, 3}, -3, new long[] {1, 2, 3}); + testRotate(new long[] {1, 2, 3}, -2, new long[] {3, 1, 2}); + testRotate(new long[] {1, 2, 3}, -1, new long[] {2, 3, 1}); + testRotate(new long[] {1, 2, 3}, 0, new long[] {1, 2, 3}); + testRotate(new long[] {1, 2, 3}, 1, new long[] {3, 1, 2}); + testRotate(new long[] {1, 2, 3}, 2, new long[] {2, 3, 1}); + testRotate(new long[] {1, 2, 3}, 3, new long[] {1, 2, 3}); + testRotate(new long[] {1, 2, 3}, 4, new long[] {3, 1, 2}); + testRotate(new long[] {1, 2, 3}, 5, new long[] {2, 3, 1}); + + testRotate(new long[] {1, 2, 3, 4}, -9, new long[] {2, 3, 4, 1}); + testRotate(new long[] {1, 2, 3, 4}, -5, new long[] {2, 3, 4, 1}); + testRotate(new long[] {1, 2, 3, 4}, -1, new long[] {2, 3, 4, 1}); + testRotate(new long[] {1, 2, 3, 4}, 0, new long[] {1, 2, 3, 4}); + testRotate(new long[] {1, 2, 3, 4}, 1, new long[] {4, 1, 2, 3}); + testRotate(new long[] {1, 2, 3, 4}, 5, new long[] {4, 1, 2, 3}); + testRotate(new long[] {1, 2, 3, 4}, 9, new long[] {4, 1, 2, 3}); + + testRotate(new long[] {1, 2, 3, 4, 5}, -6, new long[] {2, 3, 4, 5, 1}); + testRotate(new long[] {1, 2, 3, 4, 5}, -4, new long[] {5, 1, 2, 3, 4}); + testRotate(new long[] {1, 2, 3, 4, 5}, -3, new long[] {4, 5, 1, 2, 3}); + testRotate(new long[] {1, 2, 3, 4, 5}, -1, new long[] {2, 3, 4, 5, 1}); + testRotate(new long[] {1, 2, 3, 4, 5}, 0, new long[] {1, 2, 3, 4, 5}); + testRotate(new long[] {1, 2, 3, 4, 5}, 1, new long[] {5, 1, 2, 3, 4}); + testRotate(new long[] {1, 2, 3, 4, 5}, 3, new long[] {3, 4, 5, 1, 2}); + testRotate(new long[] {1, 2, 3, 4, 5}, 4, new long[] {2, 3, 4, 5, 1}); + testRotate(new long[] {1, 2, 3, 4, 5}, 6, new long[] {5, 1, 2, 3, 4}); + } + + public void testRotateIndexed() { + testRotate(new long[] {}, 0, 0, 0, new long[] {}); + + testRotate(new long[] {1}, 0, 0, 1, new long[] {1}); + testRotate(new long[] {1}, 1, 0, 1, new long[] {1}); + testRotate(new long[] {1}, 1, 1, 1, new long[] {1}); + + // Rotate the central 5 elements, leaving the ends as-is + testRotate(new long[] {0, 1, 2, 3, 4, 5, 6}, -6, 1, 6, new long[] {0, 2, 3, 4, 5, 1, 6}); + testRotate(new long[] {0, 1, 2, 3, 4, 5, 6}, -1, 1, 6, new long[] {0, 2, 3, 4, 5, 1, 6}); + testRotate(new long[] {0, 1, 2, 3, 4, 5, 6}, 0, 1, 6, new long[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new long[] {0, 1, 2, 3, 4, 5, 6}, 5, 1, 6, new long[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new long[] {0, 1, 2, 3, 4, 5, 6}, 14, 1, 6, new long[] {0, 2, 3, 4, 5, 1, 6}); + + // Rotate the first three elements + testRotate(new long[] {0, 1, 2, 3, 4, 5, 6}, -2, 0, 3, new long[] {2, 0, 1, 3, 4, 5, 6}); + testRotate(new long[] {0, 1, 2, 3, 4, 5, 6}, -1, 0, 3, new long[] {1, 2, 0, 3, 4, 5, 6}); + testRotate(new long[] {0, 1, 2, 3, 4, 5, 6}, 0, 0, 3, new long[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new long[] {0, 1, 2, 3, 4, 5, 6}, 1, 0, 3, new long[] {2, 0, 1, 3, 4, 5, 6}); + testRotate(new long[] {0, 1, 2, 3, 4, 5, 6}, 2, 0, 3, new long[] {1, 2, 0, 3, 4, 5, 6}); + + // Rotate the last four elements + testRotate(new long[] {0, 1, 2, 3, 4, 5, 6}, -6, 3, 7, new long[] {0, 1, 2, 5, 6, 3, 4}); + testRotate(new long[] {0, 1, 2, 3, 4, 5, 6}, -5, 3, 7, new long[] {0, 1, 2, 4, 5, 6, 3}); + testRotate(new long[] {0, 1, 2, 3, 4, 5, 6}, -4, 3, 7, new long[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new long[] {0, 1, 2, 3, 4, 5, 6}, -3, 3, 7, new long[] {0, 1, 2, 6, 3, 4, 5}); + testRotate(new long[] {0, 1, 2, 3, 4, 5, 6}, -2, 3, 7, new long[] {0, 1, 2, 5, 6, 3, 4}); + testRotate(new long[] {0, 1, 2, 3, 4, 5, 6}, -1, 3, 7, new long[] {0, 1, 2, 4, 5, 6, 3}); + testRotate(new long[] {0, 1, 2, 3, 4, 5, 6}, 0, 3, 7, new long[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new long[] {0, 1, 2, 3, 4, 5, 6}, 1, 3, 7, new long[] {0, 1, 2, 6, 3, 4, 5}); + testRotate(new long[] {0, 1, 2, 3, 4, 5, 6}, 2, 3, 7, new long[] {0, 1, 2, 5, 6, 3, 4}); + testRotate(new long[] {0, 1, 2, 3, 4, 5, 6}, 3, 3, 7, new long[] {0, 1, 2, 4, 5, 6, 3}); + } + public void testSortDescending() { testSortDescending(new long[] {}, new long[] {}); testSortDescending(new long[] {1}, new long[] {1}); diff --git a/guava-tests/test/com/google/common/primitives/ShortsTest.java b/guava-tests/test/com/google/common/primitives/ShortsTest.java index 287974ae87ba..f7e534659432 100644 --- a/guava-tests/test/com/google/common/primitives/ShortsTest.java +++ b/guava-tests/test/com/google/common/primitives/ShortsTest.java @@ -366,6 +366,103 @@ public void testReverseIndexed() { testReverse(new short[] {-1, 1, -2, 2}, 1, 3, new short[] {-1, -2, 1, 2}); } + private static void testRotate(short[] input, int distance, short[] expectedOutput) { + input = Arrays.copyOf(input, input.length); + Shorts.rotate(input, distance); + assertThat(input).isEqualTo(expectedOutput); + } + + private static void testRotate( + short[] input, int distance, int fromIndex, int toIndex, short[] expectedOutput) { + input = Arrays.copyOf(input, input.length); + Shorts.rotate(input, distance, fromIndex, toIndex); + assertThat(input).isEqualTo(expectedOutput); + } + + public void testRotate() { + testRotate(new short[] {}, -1, new short[] {}); + testRotate(new short[] {}, 0, new short[] {}); + testRotate(new short[] {}, 1, new short[] {}); + + testRotate(new short[] {1}, -2, new short[] {1}); + testRotate(new short[] {1}, -1, new short[] {1}); + testRotate(new short[] {1}, 0, new short[] {1}); + testRotate(new short[] {1}, 1, new short[] {1}); + testRotate(new short[] {1}, 2, new short[] {1}); + + testRotate(new short[] {1, 2}, -3, new short[] {2, 1}); + testRotate(new short[] {1, 2}, -1, new short[] {2, 1}); + testRotate(new short[] {1, 2}, -2, new short[] {1, 2}); + testRotate(new short[] {1, 2}, 0, new short[] {1, 2}); + testRotate(new short[] {1, 2}, 1, new short[] {2, 1}); + testRotate(new short[] {1, 2}, 2, new short[] {1, 2}); + testRotate(new short[] {1, 2}, 3, new short[] {2, 1}); + + testRotate(new short[] {1, 2, 3}, -5, new short[] {3, 1, 2}); + testRotate(new short[] {1, 2, 3}, -4, new short[] {2, 3, 1}); + testRotate(new short[] {1, 2, 3}, -3, new short[] {1, 2, 3}); + testRotate(new short[] {1, 2, 3}, -2, new short[] {3, 1, 2}); + testRotate(new short[] {1, 2, 3}, -1, new short[] {2, 3, 1}); + testRotate(new short[] {1, 2, 3}, 0, new short[] {1, 2, 3}); + testRotate(new short[] {1, 2, 3}, 1, new short[] {3, 1, 2}); + testRotate(new short[] {1, 2, 3}, 2, new short[] {2, 3, 1}); + testRotate(new short[] {1, 2, 3}, 3, new short[] {1, 2, 3}); + testRotate(new short[] {1, 2, 3}, 4, new short[] {3, 1, 2}); + testRotate(new short[] {1, 2, 3}, 5, new short[] {2, 3, 1}); + + testRotate(new short[] {1, 2, 3, 4}, -9, new short[] {2, 3, 4, 1}); + testRotate(new short[] {1, 2, 3, 4}, -5, new short[] {2, 3, 4, 1}); + testRotate(new short[] {1, 2, 3, 4}, -1, new short[] {2, 3, 4, 1}); + testRotate(new short[] {1, 2, 3, 4}, 0, new short[] {1, 2, 3, 4}); + testRotate(new short[] {1, 2, 3, 4}, 1, new short[] {4, 1, 2, 3}); + testRotate(new short[] {1, 2, 3, 4}, 5, new short[] {4, 1, 2, 3}); + testRotate(new short[] {1, 2, 3, 4}, 9, new short[] {4, 1, 2, 3}); + + testRotate(new short[] {1, 2, 3, 4, 5}, -6, new short[] {2, 3, 4, 5, 1}); + testRotate(new short[] {1, 2, 3, 4, 5}, -4, new short[] {5, 1, 2, 3, 4}); + testRotate(new short[] {1, 2, 3, 4, 5}, -3, new short[] {4, 5, 1, 2, 3}); + testRotate(new short[] {1, 2, 3, 4, 5}, -1, new short[] {2, 3, 4, 5, 1}); + testRotate(new short[] {1, 2, 3, 4, 5}, 0, new short[] {1, 2, 3, 4, 5}); + testRotate(new short[] {1, 2, 3, 4, 5}, 1, new short[] {5, 1, 2, 3, 4}); + testRotate(new short[] {1, 2, 3, 4, 5}, 3, new short[] {3, 4, 5, 1, 2}); + testRotate(new short[] {1, 2, 3, 4, 5}, 4, new short[] {2, 3, 4, 5, 1}); + testRotate(new short[] {1, 2, 3, 4, 5}, 6, new short[] {5, 1, 2, 3, 4}); + } + + public void testRotateIndexed() { + testRotate(new short[] {}, 0, 0, 0, new short[] {}); + + testRotate(new short[] {1}, 0, 0, 1, new short[] {1}); + testRotate(new short[] {1}, 1, 0, 1, new short[] {1}); + testRotate(new short[] {1}, 1, 1, 1, new short[] {1}); + + // Rotate the central 5 elements, leaving the ends as-is + testRotate(new short[] {0, 1, 2, 3, 4, 5, 6}, -6, 1, 6, new short[] {0, 2, 3, 4, 5, 1, 6}); + testRotate(new short[] {0, 1, 2, 3, 4, 5, 6}, -1, 1, 6, new short[] {0, 2, 3, 4, 5, 1, 6}); + testRotate(new short[] {0, 1, 2, 3, 4, 5, 6}, 0, 1, 6, new short[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new short[] {0, 1, 2, 3, 4, 5, 6}, 5, 1, 6, new short[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new short[] {0, 1, 2, 3, 4, 5, 6}, 14, 1, 6, new short[] {0, 2, 3, 4, 5, 1, 6}); + + // Rotate the first three elements + testRotate(new short[] {0, 1, 2, 3, 4, 5, 6}, -2, 0, 3, new short[] {2, 0, 1, 3, 4, 5, 6}); + testRotate(new short[] {0, 1, 2, 3, 4, 5, 6}, -1, 0, 3, new short[] {1, 2, 0, 3, 4, 5, 6}); + testRotate(new short[] {0, 1, 2, 3, 4, 5, 6}, 0, 0, 3, new short[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new short[] {0, 1, 2, 3, 4, 5, 6}, 1, 0, 3, new short[] {2, 0, 1, 3, 4, 5, 6}); + testRotate(new short[] {0, 1, 2, 3, 4, 5, 6}, 2, 0, 3, new short[] {1, 2, 0, 3, 4, 5, 6}); + + // Rotate the last four elements + testRotate(new short[] {0, 1, 2, 3, 4, 5, 6}, -6, 3, 7, new short[] {0, 1, 2, 5, 6, 3, 4}); + testRotate(new short[] {0, 1, 2, 3, 4, 5, 6}, -5, 3, 7, new short[] {0, 1, 2, 4, 5, 6, 3}); + testRotate(new short[] {0, 1, 2, 3, 4, 5, 6}, -4, 3, 7, new short[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new short[] {0, 1, 2, 3, 4, 5, 6}, -3, 3, 7, new short[] {0, 1, 2, 6, 3, 4, 5}); + testRotate(new short[] {0, 1, 2, 3, 4, 5, 6}, -2, 3, 7, new short[] {0, 1, 2, 5, 6, 3, 4}); + testRotate(new short[] {0, 1, 2, 3, 4, 5, 6}, -1, 3, 7, new short[] {0, 1, 2, 4, 5, 6, 3}); + testRotate(new short[] {0, 1, 2, 3, 4, 5, 6}, 0, 3, 7, new short[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new short[] {0, 1, 2, 3, 4, 5, 6}, 1, 3, 7, new short[] {0, 1, 2, 6, 3, 4, 5}); + testRotate(new short[] {0, 1, 2, 3, 4, 5, 6}, 2, 3, 7, new short[] {0, 1, 2, 5, 6, 3, 4}); + testRotate(new short[] {0, 1, 2, 3, 4, 5, 6}, 3, 3, 7, new short[] {0, 1, 2, 4, 5, 6, 3}); + } + public void testSortDescending() { testSortDescending(new short[] {}, new short[] {}); testSortDescending(new short[] {1}, new short[] {1}); diff --git a/guava/src/com/google/common/primitives/Booleans.java b/guava/src/com/google/common/primitives/Booleans.java index 522049bfe3eb..e637550ad91b 100644 --- a/guava/src/com/google/common/primitives/Booleans.java +++ b/guava/src/com/google/common/primitives/Booleans.java @@ -551,4 +551,54 @@ public static void reverse(boolean[] array, int fromIndex, int toIndex) { array[j] = tmp; } } + + /** + * Performs a right rotation of {@code array} of "distance" places, so that the first element is + * moved to index "distance", and the element at index {@code i} ends up at index {@code (distance + * + i) mod array.length}. This is equivalent to {@code Collections.rotate(Booleans.asList(array), + * distance)}, but is somewhat faster. + * + *

The provided "distance" may be negative, which will rotate left. + * + * @since NEXT + */ + public static void rotate(boolean[] array, int distance) { + rotate(array, distance, 0, array.length); + } + + /** + * Performs a right rotation of {@code array} between {@code fromIndex} inclusive and {@code + * toIndex} exclusive. This is equivalent to {@code + * Collections.rotate(Booleans.asList(array).subList(fromIndex, toIndex), distance)}, but is + * somewhat faster. + * + *

The provided "distance" may be negative, which will rotate left. + * + * @throws IndexOutOfBoundsException if {@code fromIndex < 0}, {@code toIndex > array.length}, or + * {@code toIndex > fromIndex} + * @since NEXT + */ + public static void rotate(boolean[] array, int distance, int fromIndex, int toIndex) { + // See Ints.rotate for more details about possible algorithms here. + checkNotNull(array); + checkPositionIndexes(fromIndex, toIndex, array.length); + if (array.length <= 1) { + return; + } + + int length = toIndex - fromIndex; + // Obtain m = (-distance mod length), a non-negative value less than "length". This is how many + // places left to rotate. + int m = -distance % length; + m = (m < 0) ? m + length : m; + // The current index of what will become the first element of the rotated section. + int newFirstIndex = m + fromIndex; + if (newFirstIndex == fromIndex) { + return; + } + + reverse(array, fromIndex, newFirstIndex); + reverse(array, newFirstIndex, toIndex); + reverse(array, fromIndex, toIndex); + } } diff --git a/guava/src/com/google/common/primitives/Bytes.java b/guava/src/com/google/common/primitives/Bytes.java index 62997f34aac7..11832742e5fa 100644 --- a/guava/src/com/google/common/primitives/Bytes.java +++ b/guava/src/com/google/common/primitives/Bytes.java @@ -396,4 +396,54 @@ public static void reverse(byte[] array, int fromIndex, int toIndex) { array[j] = tmp; } } + + /** + * Performs a right rotation of {@code array} of "distance" places, so that the first element is + * moved to index "distance", and the element at index {@code i} ends up at index {@code (distance + * + i) mod array.length}. This is equivalent to {@code Collections.rotate(Bytes.asList(array), + * distance)}, but is somewhat faster. + * + *

The provided "distance" may be negative, which will rotate left. + * + * @since NEXT + */ + public static void rotate(byte[] array, int distance) { + rotate(array, distance, 0, array.length); + } + + /** + * Performs a right rotation of {@code array} between {@code fromIndex} inclusive and {@code + * toIndex} exclusive. This is equivalent to {@code + * Collections.rotate(Bytes.asList(array).subList(fromIndex, toIndex), distance)}, but is somewhat + * faster. + * + *

The provided "distance" may be negative, which will rotate left. + * + * @throws IndexOutOfBoundsException if {@code fromIndex < 0}, {@code toIndex > array.length}, or + * {@code toIndex > fromIndex} + * @since NEXT + */ + public static void rotate(byte[] array, int distance, int fromIndex, int toIndex) { + // See Ints.rotate for more details about possible algorithms here. + checkNotNull(array); + checkPositionIndexes(fromIndex, toIndex, array.length); + if (array.length <= 1) { + return; + } + + int length = toIndex - fromIndex; + // Obtain m = (-distance mod length), a non-negative value less than "length". This is how many + // places left to rotate. + int m = -distance % length; + m = (m < 0) ? m + length : m; + // The current index of what will become the first element of the rotated section. + int newFirstIndex = m + fromIndex; + if (newFirstIndex == fromIndex) { + return; + } + + reverse(array, fromIndex, newFirstIndex); + reverse(array, newFirstIndex, toIndex); + reverse(array, fromIndex, toIndex); + } } diff --git a/guava/src/com/google/common/primitives/Chars.java b/guava/src/com/google/common/primitives/Chars.java index 4a2e3a3449cb..c677021f1aa4 100644 --- a/guava/src/com/google/common/primitives/Chars.java +++ b/guava/src/com/google/common/primitives/Chars.java @@ -488,6 +488,56 @@ public static void reverse(char[] array, int fromIndex, int toIndex) { } } + /** + * Performs a right rotation of {@code array} of "distance" places, so that the first element is + * moved to index "distance", and the element at index {@code i} ends up at index {@code (distance + * + i) mod array.length}. This is equivalent to {@code Collections.rotate(Chars.asList(array), + * distance)}, but is considerably faster and avoids allocation and garbage collection. + * + *

The provided "distance" may be negative, which will rotate left. + * + * @since NEXT + */ + public static void rotate(char[] array, int distance) { + rotate(array, distance, 0, array.length); + } + + /** + * Performs a right rotation of {@code array} between {@code fromIndex} inclusive and {@code + * toIndex} exclusive. This is equivalent to {@code + * Collections.rotate(Chars.asList(array).subList(fromIndex, toIndex), distance)}, but is + * considerably faster and avoids allocations and garbage collection. + * + *

The provided "distance" may be negative, which will rotate left. + * + * @throws IndexOutOfBoundsException if {@code fromIndex < 0}, {@code toIndex > array.length}, or + * {@code toIndex > fromIndex} + * @since NEXT + */ + public static void rotate(char[] array, int distance, int fromIndex, int toIndex) { + // See Ints.rotate for more details about possible algorithms here. + checkNotNull(array); + checkPositionIndexes(fromIndex, toIndex, array.length); + if (array.length <= 1) { + return; + } + + int length = toIndex - fromIndex; + // Obtain m = (-distance mod length), a non-negative value less than "length". This is how many + // places left to rotate. + int m = -distance % length; + m = (m < 0) ? m + length : m; + // The current index of what will become the first element of the rotated section. + int newFirstIndex = m + fromIndex; + if (newFirstIndex == fromIndex) { + return; + } + + reverse(array, fromIndex, newFirstIndex); + reverse(array, newFirstIndex, toIndex); + reverse(array, fromIndex, toIndex); + } + /** * Returns a fixed-size list backed by the specified array, similar to {@link * Arrays#asList(Object[])}. The list supports {@link List#set(int, Object)}, but any attempt to diff --git a/guava/src/com/google/common/primitives/Doubles.java b/guava/src/com/google/common/primitives/Doubles.java index fab3cf610a79..19beb5d25a50 100644 --- a/guava/src/com/google/common/primitives/Doubles.java +++ b/guava/src/com/google/common/primitives/Doubles.java @@ -469,6 +469,56 @@ public static void reverse(double[] array, int fromIndex, int toIndex) { } } + /** + * Performs a right rotation of {@code array} of "distance" places, so that the first element is + * moved to index "distance", and the element at index {@code i} ends up at index {@code (distance + * + i) mod array.length}. This is equivalent to {@code Collections.rotate(Bytes.asList(array), + * distance)}, but is considerably faster and avoids allocation and garbage collection. + * + *

The provided "distance" may be negative, which will rotate left. + * + * @since NEXT + */ + public static void rotate(double[] array, int distance) { + rotate(array, distance, 0, array.length); + } + + /** + * Performs a right rotation of {@code array} between {@code fromIndex} inclusive and {@code + * toIndex} exclusive. This is equivalent to {@code + * Collections.rotate(Bytes.asList(array).subList(fromIndex, toIndex), distance)}, but is + * considerably faster and avoids allocations and garbage collection. + * + *

The provided "distance" may be negative, which will rotate left. + * + * @throws IndexOutOfBoundsException if {@code fromIndex < 0}, {@code toIndex > array.length}, or + * {@code toIndex > fromIndex} + * @since NEXT + */ + public static void rotate(double[] array, int distance, int fromIndex, int toIndex) { + // See Ints.rotate for more details about possible algorithms here. + checkNotNull(array); + checkPositionIndexes(fromIndex, toIndex, array.length); + if (array.length <= 1) { + return; + } + + int length = toIndex - fromIndex; + // Obtain m = (-distance mod length), a non-negative value less than "length". This is how many + // places left to rotate. + int m = -distance % length; + m = (m < 0) ? m + length : m; + // The current index of what will become the first element of the rotated section. + int newFirstIndex = m + fromIndex; + if (newFirstIndex == fromIndex) { + return; + } + + reverse(array, fromIndex, newFirstIndex); + reverse(array, newFirstIndex, toIndex); + reverse(array, fromIndex, toIndex); + } + /** * Returns an array containing each value of {@code collection}, converted to a {@code double} * value in the manner of {@link Number#doubleValue}. diff --git a/guava/src/com/google/common/primitives/Floats.java b/guava/src/com/google/common/primitives/Floats.java index b038cb289636..a9a44d6e3532 100644 --- a/guava/src/com/google/common/primitives/Floats.java +++ b/guava/src/com/google/common/primitives/Floats.java @@ -464,6 +464,56 @@ public static void reverse(float[] array, int fromIndex, int toIndex) { } } + /** + * Performs a right rotation of {@code array} of "distance" places, so that the first element is + * moved to index "distance", and the element at index {@code i} ends up at index {@code (distance + * + i) mod array.length}. This is equivalent to {@code Collections.rotate(Floats.asList(array), + * distance)}, but is considerably faster and avoids allocation and garbage collection. + * + *

The provided "distance" may be negative, which will rotate left. + * + * @since NEXT + */ + public static void rotate(float[] array, int distance) { + rotate(array, distance, 0, array.length); + } + + /** + * Performs a right rotation of {@code array} between {@code fromIndex} inclusive and {@code + * toIndex} exclusive. This is equivalent to {@code + * Collections.rotate(Floats.asList(array).subList(fromIndex, toIndex), distance)}, but is + * considerably faster and avoids allocations and garbage collection. + * + *

The provided "distance" may be negative, which will rotate left. + * + * @throws IndexOutOfBoundsException if {@code fromIndex < 0}, {@code toIndex > array.length}, or + * {@code toIndex > fromIndex} + * @since NEXT + */ + public static void rotate(float[] array, int distance, int fromIndex, int toIndex) { + // See Ints.rotate for more details about possible algorithms here. + checkNotNull(array); + checkPositionIndexes(fromIndex, toIndex, array.length); + if (array.length <= 1) { + return; + } + + int length = toIndex - fromIndex; + // Obtain m = (-distance mod length), a non-negative value less than "length". This is how many + // places left to rotate. + int m = -distance % length; + m = (m < 0) ? m + length : m; + // The current index of what will become the first element of the rotated section. + int newFirstIndex = m + fromIndex; + if (newFirstIndex == fromIndex) { + return; + } + + reverse(array, fromIndex, newFirstIndex); + reverse(array, newFirstIndex, toIndex); + reverse(array, fromIndex, toIndex); + } + /** * Returns an array containing each value of {@code collection}, converted to a {@code float} * value in the manner of {@link Number#floatValue}. diff --git a/guava/src/com/google/common/primitives/Ints.java b/guava/src/com/google/common/primitives/Ints.java index b4b671b252fe..6aadc5d846c9 100644 --- a/guava/src/com/google/common/primitives/Ints.java +++ b/guava/src/com/google/common/primitives/Ints.java @@ -539,6 +539,33 @@ public static void rotate(int[] array, int distance) { * @since NEXT */ public static void rotate(int[] array, int distance, int fromIndex, int toIndex) { + // There are several well-known algorithms for rotating part of an array (or, equivalently, + // exchanging two blocks of memory). This classic text by Gries and Mills mentions several: + // https://ecommons.cornell.edu/bitstream/handle/1813/6292/81-452.pdf. + // (1) "Reversal", the one we have here. + // (2) "Dolphin". If we're rotating an array a of size n by a distance of d, then element a[0] + // ends up at a[d], which in turn ends up at a[2d], and so on until we get back to a[0]. + // (All indices taken mod n.) If d and n are mutually prime, all elements will have been + // moved at that point. Otherwise, we can rotate the cycle a[1], a[1 + d], a[1 + 2d], etc, + // then a[2] etc, and so on until we have rotated all elements. There are gcd(d, n) cycles + // in all. + // (3) "Successive". We can consider that we are exchanging a block of size d (a[0..d-1]) with a + // block of size n-d (a[d..n-1]), where in general these blocks have different sizes. If we + // imagine a line separating the first block from the second, we can proceed by exchanging + // the smaller of these blocks with the far end of the other one. That leaves us with a + // smaller version of the same problem. + // Say we are rotating abcdefgh by 5. We start with abcde|fgh. The smaller block is [fgh]: + // [abc]de|[fgh] -> [fgh]de|[abc]. Now [fgh] is in the right place, but we need to swap [de] + // with [abc]: fgh[de]|a[bc] -> fgh[bc]|a[de]. Now we need to swap [a] with [bc]: + // fgh[b]c|[a]de -> fgh[a]c|[b]de. Finally we need to swap [c] with [b]: + // fgha[c]|[b]de -> fgha[b]|[c]de. Because these two blocks are the same size, we are done. + // The Dolphin algorithm is attractive because it does the fewest array reads and writes: each + // array slot is read and written exactly once. However, it can have very poor memory locality: + // benchmarking shows it can take 7 times longer than the other two in some cases. The other two + // do n swaps, minus a delta (0 or 2 for Reversal, gcd(d, n) for Successive), so that's about + // twice as many reads and writes. But benchmarking shows that they usually perform better than + // Dolphin. Reversal is about as good as Successive on average, and it is much simpler, + // especially since we already have a `reverse` method. checkNotNull(array); checkPositionIndexes(fromIndex, toIndex, array.length); if (array.length <= 1) { diff --git a/guava/src/com/google/common/primitives/Longs.java b/guava/src/com/google/common/primitives/Longs.java index 98c055e27ac3..1dbae0ec743e 100644 --- a/guava/src/com/google/common/primitives/Longs.java +++ b/guava/src/com/google/common/primitives/Longs.java @@ -608,6 +608,56 @@ public static void reverse(long[] array, int fromIndex, int toIndex) { } } + /** + * Performs a right rotation of {@code array} of "distance" places, so that the first element is + * moved to index "distance", and the element at index {@code i} ends up at index {@code (distance + * + i) mod array.length}. This is equivalent to {@code Collections.rotate(Longs.asList(array), + * distance)}, but is considerably faster and avoids allocation and garbage collection. + * + *

The provided "distance" may be negative, which will rotate left. + * + * @since NEXT + */ + public static void rotate(long[] array, int distance) { + rotate(array, distance, 0, array.length); + } + + /** + * Performs a right rotation of {@code array} between {@code fromIndex} inclusive and {@code + * toIndex} exclusive. This is equivalent to {@code + * Collections.rotate(Longs.asList(array).subList(fromIndex, toIndex), distance)}, but is + * considerably faster and avoids allocations and garbage collection. + * + *

The provided "distance" may be negative, which will rotate left. + * + * @throws IndexOutOfBoundsException if {@code fromIndex < 0}, {@code toIndex > array.length}, or + * {@code toIndex > fromIndex} + * @since NEXT + */ + public static void rotate(long[] array, int distance, int fromIndex, int toIndex) { + // See Ints.rotate for more details about possible algorithms here. + checkNotNull(array); + checkPositionIndexes(fromIndex, toIndex, array.length); + if (array.length <= 1) { + return; + } + + int length = toIndex - fromIndex; + // Obtain m = (-distance mod length), a non-negative value less than "length". This is how many + // places left to rotate. + int m = -distance % length; + m = (m < 0) ? m + length : m; + // The current index of what will become the first element of the rotated section. + int newFirstIndex = m + fromIndex; + if (newFirstIndex == fromIndex) { + return; + } + + reverse(array, fromIndex, newFirstIndex); + reverse(array, newFirstIndex, toIndex); + reverse(array, fromIndex, toIndex); + } + /** * Returns an array containing each value of {@code collection}, converted to a {@code long} value * in the manner of {@link Number#longValue}. diff --git a/guava/src/com/google/common/primitives/Shorts.java b/guava/src/com/google/common/primitives/Shorts.java index 09e0f7cfc312..a53108672134 100644 --- a/guava/src/com/google/common/primitives/Shorts.java +++ b/guava/src/com/google/common/primitives/Shorts.java @@ -512,6 +512,56 @@ public static void reverse(short[] array, int fromIndex, int toIndex) { } } + /** + * Performs a right rotation of {@code array} of "distance" places, so that the first element is + * moved to index "distance", and the element at index {@code i} ends up at index {@code (distance + * + i) mod array.length}. This is equivalent to {@code Collections.rotate(Shorts.asList(array), + * distance)}, but is considerably faster and avoids allocation and garbage collection. + * + *

The provided "distance" may be negative, which will rotate left. + * + * @since NEXT + */ + public static void rotate(short[] array, int distance) { + rotate(array, distance, 0, array.length); + } + + /** + * Performs a right rotation of {@code array} between {@code fromIndex} inclusive and {@code + * toIndex} exclusive. This is equivalent to {@code + * Collections.rotate(Shorts.asList(array).subList(fromIndex, toIndex), distance)}, but is + * considerably faster and avoids allocations and garbage collection. + * + *

The provided "distance" may be negative, which will rotate left. + * + * @throws IndexOutOfBoundsException if {@code fromIndex < 0}, {@code toIndex > array.length}, or + * {@code toIndex > fromIndex} + * @since NEXT + */ + public static void rotate(short[] array, int distance, int fromIndex, int toIndex) { + // See Ints.rotate for more details about possible algorithms here. + checkNotNull(array); + checkPositionIndexes(fromIndex, toIndex, array.length); + if (array.length <= 1) { + return; + } + + int length = toIndex - fromIndex; + // Obtain m = (-distance mod length), a non-negative value less than "length". This is how many + // places left to rotate. + int m = -distance % length; + m = (m < 0) ? m + length : m; + // The current index of what will become the first element of the rotated section. + int newFirstIndex = m + fromIndex; + if (newFirstIndex == fromIndex) { + return; + } + + reverse(array, fromIndex, newFirstIndex); + reverse(array, newFirstIndex, toIndex); + reverse(array, fromIndex, toIndex); + } + /** * Returns an array containing each value of {@code collection}, converted to a {@code short} * value in the manner of {@link Number#shortValue}.