@@ -1142,6 +1142,44 @@ defmodule StreamData do
11421142 end
11431143 end
11441144
1145+ @ doc """
1146+ Generates lists with the same elements as the provided `list` but in a random order.
1147+
1148+ ## Examples
1149+
1150+ StreamData.shuffle([1, 2, 3, 4, 5])
1151+ |> Enum.take(3)
1152+ #=> [[4, 2, 5, 3, 1], [1, 3, 4, 5, 2], [3, 2, 5, 4, 1]]
1153+
1154+ ## Shrinking
1155+
1156+ Shrinks towards not changed lists.
1157+ """
1158+ def shuffle ( [ ] ) , do: constant ( [ ] )
1159+
1160+ def shuffle ( list ) do
1161+ # convert to array for faster swapping
1162+ array = :array . from_list ( list )
1163+ l = :array . size ( array )
1164+
1165+ # Inspired by this clojure implementation:
1166+ # https://github.com/clojure/test.check/blob/0ee576eb73d4864c199305c4a0c1e8101d8d1b39/src/main/clojure/clojure/test/check/generators.cljc#L636
1167+ list_of ( { integer ( 0 .. ( l - 1 ) ) , integer ( 0 .. ( l - 1 ) ) } , length: 0 .. ( 2 * l ) )
1168+ |> map ( fn swap_instructions ->
1169+ Enum . reduce ( swap_instructions , array , fn { i , j } , array ->
1170+ array_swap ( array , i , j )
1171+ end )
1172+ |> :array . to_list ( )
1173+ end )
1174+ end
1175+
1176+ defp array_swap ( array , i , j ) do
1177+ v_i = :array . get ( i , array )
1178+ v_j = :array . get ( j , array )
1179+ array = :array . set ( i , v_j , array )
1180+ :array . set ( j , v_i , array )
1181+ end
1182+
11451183 @ doc ~S"""
11461184 Generates non-empty improper lists where elements of the list are generated
11471185 out of `first` and the improper ending out of `improper`.
0 commit comments