Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions src/arcade/core/util/Utilities.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
package arcade.core.util;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.ListIterator;
import java.util.Set;
import sim.util.Bag;
import ec.util.MersenneTwisterFast;

/** Container class for utility methods. */
Expand Down Expand Up @@ -65,4 +70,49 @@ static void swap(Object[] arr, int i, int j) {
arr[i] = arr[j];
arr[j] = temp;
}

/**
* Calculates the fraction of elements in one collection that are also present in another
* collection. Duplicate elements in the numeratorCollection are treated as one occurrence,
* while duplicates in denominatorCollection are counted individually in both total number of
* matches and size. Returns 0 if either list is empty.
*
* @param <T> the element type present in the two collections
Comment thread
navyacodes marked this conversation as resolved.
* @param numeratorCollection the collection to check membership against, items be the same type
* as denominatorCollection
* @param denominatorCollection the collection whose fraction is being calculated (denominator)
* @return fraction of elements in denominatorCollection that are also found in
* numeratorCollection
*/
public static <T> double getCollectionFraction(
Collection<T> numeratorCollection, Collection<T> denominatorCollection) {
if (denominatorCollection.isEmpty() || numeratorCollection.isEmpty()) {
return 0;
}
double overlap = 0;
Set<T> numeratorSet = new HashSet<>(numeratorCollection);
for (T item : denominatorCollection) {
if (numeratorSet.contains(item)) {
overlap++;
}
}
return overlap / denominatorCollection.size();
}

/**
* Converts the given Bag into a typed Collection, with each element being cast to the given
* type.
*
* @param <T> the target element type
Comment thread
navyacodes marked this conversation as resolved.
* @param bag the bag to convert
* @param type the class object representing the target element type
* @return a new Collection<T> containing the bag's elements, cast to provided type
*/
public static <T> Collection<T> convertToCollection(Bag bag, Class<T> type) {
List<T> list = new ArrayList<>(bag.numObjs);
for (int i = 0; i < bag.numObjs; i++) {
list.add(type.cast(bag.objs[i]));
}
return list;
}
}
215 changes: 215 additions & 0 deletions test/arcade/core/util/UtilitiesTest.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package arcade.core.util;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import org.junit.jupiter.api.Test;
import sim.util.Bag;
import ec.util.MersenneTwisterFast;
import static org.junit.jupiter.api.Assertions.*;
import static arcade.core.ARCADETestUtilities.*;
Expand Down Expand Up @@ -94,4 +97,216 @@ public void shuffleList_givenSameSeed_shufflesSame() {
// Check that both shuffled lists are the same.
assertEquals(list1, list2);
}

@Test
public void getCollectionFraction_emptyLists_returnsZero() {
List<Integer> list1 = new ArrayList<>();
List<Integer> list2 = new ArrayList<>();

double fraction = getCollectionFraction(list1, list2);

assertEquals(0, fraction);
}

@Test
public void getCollectionFraction_noOverlapWithList2_returnsZero() {
List<Integer> items = new ArrayList<>();
items.add(1);
items.add(2);

ArrayList<Integer> otherItems = new ArrayList<>(List.of(3));

double fraction = getCollectionFraction(otherItems, items);

assertEquals(0, fraction);
}

@Test
public void getCollectionFraction_allElementsInList2_returnsOne() {
List<String> items = new ArrayList<>();
String a = "a";
String b = "b";
items.add(a);
items.add(b);

ArrayList<String> otherItems = new ArrayList<>(List.of(a, b));

double fraction = getCollectionFraction(otherItems, items);

assertEquals(1, fraction);
}

@Test
public void getCollectionFraction_someElementsInList2_returnsPartialFraction() {
List<String> items = new ArrayList<>();
String a = "a";
String b = "b";
String c = "c";
String d = "d";
items.add(a);
items.add(b);
items.add(c);
items.add(d);

ArrayList<String> otherItems = new ArrayList<>(List.of(a, c));

double fraction = getCollectionFraction(otherItems, items);

assertEquals(0.5, fraction);
}

@Test
public void getCollectionFraction_emptyEitherList_returnsZero() {
List<String> items = new ArrayList<>();
items.add("a");
items.add("b");

double fraction = getCollectionFraction(new ArrayList<String>(), items);

assertEquals(0, fraction);
assertEquals(0, getCollectionFraction(items, new ArrayList<String>()));
}

@Test
public void getCollectionFraction_differentEqualObjects_countsAsMatch() {
List<String> items = new ArrayList<>();
String a = "a";
String b = "b";
String c = "c";
String d = "d";
items.add(a);
items.add(b);
items.add(c);
items.add(d);
ArrayList<String> otherItems = new ArrayList<>(List.of(new String("a")));

double fraction = getCollectionFraction(otherItems, items);

assertEquals(0.25, fraction);
}

@Test
public void getCollectionFraction_oneOfSevenMatch_returnsPreciseFraction() {
List<Integer> items = new ArrayList<>();
Integer a = 0;
for (int i = 1; i <= 6; i++) {
items.add(i);
}
items.add(a);

ArrayList<Integer> otherItems = new ArrayList<>(List.of(a));

double fraction = getCollectionFraction(otherItems, items);

assertEquals(1.0 / 7.0, fraction, EPSILON);
}

@Test
public void getCollectionFraction_sameCollectionPassed_returnsOne() {
List<Integer> items = new ArrayList<>();
for (int i = 1; i <= 6; i++) {
items.add(i);
}

double fraction = getCollectionFraction(items, items);

assertEquals(1, fraction, EPSILON);
}

@Test
public void convertToCollection_emptyBag_returnsEmptyCollection() {
Bag bag = new Bag();

Collection<String> result = convertToCollection(bag, String.class);

assertTrue(result.isEmpty());
}

@Test
public void convertToCollection_populatedBag_returnsAllElementsInOrder() {
Bag bag = new Bag();
String a = "a";
String b = "b";
String c = "c";
bag.add(a);
bag.add(b);
bag.add(c);

Collection<String> result = convertToCollection(bag, String.class);

assertEquals(3, result.size());
java.util.Iterator<String> it = result.iterator();
assertEquals(a, it.next());
assertEquals(b, it.next());
assertEquals(c, it.next());
}

@Test
public void convertToCollection_bagWithExcessCapacity_onlyIncludesNumObjsElements() {
Bag bag = new Bag(10);
String a = "a";
bag.add(a);

Collection<String> result = convertToCollection(bag, String.class);

assertEquals(1, result.size());
assertEquals(a, result.iterator().next());
}

@Test
public void convertToCollection_wrongElementType_throwsClassCastException() {
Bag bag = new Bag();
bag.add(1);

assertThrows(ClassCastException.class, () -> convertToCollection(bag, String.class));
}

@Test
public void convertToCollection_mixedElementTypes_throwsOnFirstMismatch() {
Bag bag = new Bag();
bag.add("a");
bag.add(1);

assertThrows(ClassCastException.class, () -> convertToCollection(bag, String.class));
}

@Test
public void convertToCollection_duplicateElements_preservesDuplicates() {
Bag bag = new Bag();
String a = "a";
bag.add(a);
bag.add(a);

Collection<String> result = convertToCollection(bag, String.class);

assertEquals(2, result.size());
}

@Test
public void convertToCollection_bagWithNullElement_includesNull() {
Bag bag = new Bag();
bag.add(null);

Collection<String> result = convertToCollection(bag, String.class);

assertEquals(1, result.size());
assertNull(result.iterator().next());
}

@Test
public void
convertToCollection_resultUsableWithGetCollectionFraction_computesCorrectFraction() {
Bag itemBag = new Bag();
String a = "a";
String b = "b";
itemBag.add(a);
itemBag.add(b);

List<String> otherItems = new ArrayList<>(List.of(a));

Collection<String> items = convertToCollection(itemBag, String.class);
double fraction = getCollectionFraction(otherItems, new ArrayList<>(items));

assertEquals(0.5, fraction);
}
}
Loading