Java map value comparisons - java

This could be the entirely wrong usage for the datatype, but I'm trying to create a Map-Set of some sort, so before insertion into the map I'm trying to use
//Material is custom class
Map<Integer, Material> x = TreeMap();
if(!x.containsValue(new Material))
x.put(int val, new Material);
My expectation of this is that it would compare two objects generated from the same data would return true, but it appears that this is not the case. Why is that, and is there an existing solution other than iterating through the entire map to find out if any element contains a Material where all fields are the same as the incoming?

Having a well defined class Material
public class Material extends Comparable<Material> {
final int a;
final String b;
...
public Material(int a, String b, ...) {
this.a = a;
this.b = b;
}
#Override
int compareTo(Material other) {
int cmp = Integer.compare(a, other.a);
if (cmp == 0) {
cmp = b.compareTo(other.b);
}
...
return cmp;
}
#Override
public boolean equals(Object other) {
if (other == null || !(other instanceof Material)) {
return false;
}
return compareTo((Material) other) == 0;
}
#Override int hashCode() {
return ...;
}
A Set suffices:
Set<Material> materials = new HashSet<>(); // hashCode+equals
Set<Material> materials = new TreeSet<>(); // compareTo
Now I have made the fields of Material final so the object is immutable, as changing would play havoc with the sets ordering.
For a mutable quality, like quantity:
Map<Material, Integer> stock = new HashMap<>();
Material material = new Material(...);
stock.put(material, 100);
int quantity = stock.getOrDefault(material, 0); // Java 8
int quantity = 0; // Java 7
{
Integer q = stock.get(material);
if (q != null) {
quantity = q;
}
}

Related

Grouping a list of objects into a list of lists by a boolean function using Java 8 streams

Say I have a list of objects. I want to group them into a list of lists where each inner list contains elements for which a boolean comparison function returns true:
public class VO {
public VO(int age, int val) {
this.age = age;
this.val = val;
}
public int age;
public int val;
}
public void testGrouping() {
// Equal to vo2
VO vo1 = new VO(1, 100);
// Equal to vo1 and vo3
VO vo2 = new VO(3, 105);
// Equal to vo2 but not vo1 (age difference > 2),
// so it belongs into a new bucket
VO vo3 = new VO(5, 110);
// Equal to vo3, so it belongs into the same bucket as vo3
VO vo4 = new VO(7, 116);
List<VO> values = Arrays.asList(vo1, vo2, vo3, vo4);
//Group values using isEqual(VO, VO) into buckets somehow
//Any two values in a bucket must pass the check
//Expected without any specific order:
//[[vo1, vo2, [vo3, vo4]]
}
private boolean isEqual(VO a, VO b) {
return Math.abs(a.age - b.age) <= 2 && Math.abs(a.val - b.val) <= 10;
}
This is just a simplified example of the data that I have, in reality the comparison method is more complicated than that. Important is that each object must match each other object in its bucket regarding the check. The objects cannot be grouped/mapped by a specific value.
I already have code which does this but takes three levels of for-loops and which took me about a day to write. I'm curious if this can be achieved easier with streams.
Try mapping your items first, then use Collectors.groupingBy():
values.stream().map(name -> name.split("\\s+")).collect(groupingBy(a -> a[1]));
This does not return lists but arrays - but the concept is the same.
You can modify a bit the isEqual method so that you can use it as a comparator for the TreeMap. Then you can collect the TreeMap<VO, List<VO>> as follows. This code works in Java 7:
public static int isEqual(VO a, VO b) {
if (Math.abs(a.age - b.age) > 2 || Math.abs(a.val - b.val) > 10)
return 1;
else if (Math.abs(a.age - b.age) <= 2 && Math.abs(a.val - b.val) <= 10)
return 0;
else
return -1;
}
public static void main(String[] args) {
VO vo1 = new VO(1, 100);
VO vo2 = new VO(3, 105);
VO vo3 = new VO(5, 110);
VO vo4 = new VO(7, 116);
List<VO> values = Arrays.asList(vo1, vo2, vo3, vo4);
Map<VO, List<VO>> buckets = new TreeMap<>(new Comparator<VO>() {
#Override
public int compare(VO o1, VO o2) {
return isEqual(o1, o2);
}
});
for (VO vo : values) {
List<VO> list = buckets.get(vo);
if (list == null) {
list = new ArrayList<>();
}
list.add(vo);
buckets.put(vo, list);
}
// output
System.out.println(buckets);
//{VO{1=100}=[VO{1=100}, VO{3=105}], VO{5=110}=[VO{5=110}, VO{7=116}]}
}
public static class VO {
public int age, val;
public VO(int age, int val) {
this.age = age;
this.val = val;
}
#Override
public String toString() {
return "VO{" + age + "=" + val + "}";
}
}

Java: How to use Comparator with arraylist as values of HashMap

I have this HashMap:
HashMap< itemDetails, Pair<ArrayList<itemDetails>, AssociatedInfo>> AssociatedItemCasuaList = new HashMap<>();
where its key is class, and its values consist of pair (a= arraylist of class itemDetails, b= class AssociatedInfo):
class itemDetails {
public ArrayList<Integer> itemId;
public float expectedSupport = 0;
// etc
}
and
class Pair<T, U> {
T a;
U b;
Pair(T a, U b) {
this.a = a;
this.b = b;
}
T getA() {
return a;
}
U getB() {
return b;
}
}
and
class AssociatedInfo {
public int noOfKCasual = 0;
public int AssociateListStart = 0;
public int AssociateListEnd = 0;
}
I want to sort the first pair of the the values of HashMap
which is the ArrayList<Integer> itemId in the class itemDetails
I used this Comparator
public class ItemComparator implements Comparator<ArrayList<Integer> >{
#Override
public int compare(final ArrayList<Integer> entry1, final ArrayList<Integer> entry2){
if (entry1 == null && entry2 == null)
return 0;
if (entry1 == null)
return 1;
if (entry2 == null)
return -1;
if (entry1.isEmpty() && entry2.isEmpty())
return 0;
if (entry1.isEmpty())
return 1;
if (entry2.isEmpty())
return -1;
return entry1.get(0).compareTo(entry2.get(0));
}
}
I don't know how to write Collections.sort
if (AssociatedItemCasuaList.containsKey(LHS)) {
AssociatedItemCasuaList.get(LHS).a.add(RHS2);
AssociatedItemCasuaList.get(LHS).b.AssociateListStart = 0;
AssociatedItemCasuaList.get(LHS).b.AssociateListEnd += 1;
AssociatedItemCasuaList.get(LHS).b.noOfKCasual += 1;
} else {
ArrayList<itemDetails> ArrayListRHS = new ArrayList<itemDetails>();
ArrayListRHS.add(RHS2);
AssociatedInfo AttribAssociatedInfo1 = new AssociatedInfo();
AttribAssociatedInfo1.noOfKCasual = 1;
AttribAssociatedInfo1.AssociateListStart = 0;
AttribAssociatedInfo1.AssociateListEnd = 0;
AssociatedItemCasuaList.put(LHS, new Pair(ArrayListRHS, AttribAssociatedInfo1));
}
// Collections.sort(AssociatedItemCasuaList.get(LHS), new ItemComparator());
Update:
Example:
AssociatedItemCasuaList < **key**=LHS, **value**=Pair<a, b> >
Let key=LHS:
LHS.itemId=1
LHS.expectedSupport=87.5
and values = Pair < a, b >
Let focus here in a only in this example.
a= ArrayList<itemDetails>
Let itemDetails RH2
and every time (in loop) I add RHS2 to key LHS as:
AssociatedItemCasuaList.get(LHS).a.add(RHS2)
here RHS2 takes different values every time
RHS2.itemId
RHS2.expectedSupport
until now, I don't have problem.
I want to sort the ArrayList in a (that I filled with RHS2) based on its itemId
Ignoring the fact I didn't understand a single word, what about this:
public class ItemComparator2 implements Comparator<itemDetails> {
Comparator myComp = new ItemComparator();
#Override
public int compare(itemDetails a, itemDetails b){
return myComp.compare(a.itemId, b.itemId);
}
}
// ...
Collections.sort(AssociatedItemCasuaList.get(LHS).getA(), new ItemComparator2());

Interesting HashMap implementation (build 1.7.0_25-b17)

I have developed a garbage collector friendly String cache for my Android game. Its purpose is to handle Strings for ints. I made a silly mistake implementing it but the bug never disclosed itself in desktop. In Android, however, the cache started returning funny Strings at once:
class IntStringCache {
private final Map<IntStringCache.IntCacheKey, String> cachedStrings = new HashMap<IntStringCache.IntCacheKey, String>();
private final IntCacheKey tempIntCacheKey = new IntCacheKey(0);
public String getStringFor(int i) {
tempIntCacheKey.setIntValue(i);
String stringValue = cachedStrings.get(tempIntCacheKey);
if (stringValue == null) {
stringValue = String.valueOf(i);
// ERROR - putting the same object instead of new IntCachKey(i)
cachedStrings.put(tempIntCacheKey, stringValue);
}
return stringValue;
}
public int getSize() {
return cachedStrings.size();
}
private class IntCacheKey {
private int intValue;
private IntCacheKey(int intValue) {
this.intValue = intValue;
}
private void setIntValue(int intValue) {
this.intValue = intValue;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + getOuterType().hashCode();
result = prime * result + intValue;
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
IntCacheKey other = (IntCacheKey) obj;
if (!getOuterType().equals(other.getOuterType()))
return false;
if (intValue != other.intValue)
return false;
return true;
}
private IntStringCache getOuterType() {
return IntStringCache.this;
}
}
And the tests all of which pass:
public class IntStringCacheTest {
private IntStringCache intStringCache = new IntStringCache();
#Test
public void shouldCacheString() {
// given
int i = 1;
// when
String s1 = intStringCache.getStringFor(i);
String s2 = intStringCache.getStringFor(i);
// then
assertThat(s1).isNotNull();
assertThat(s1).isEqualTo(String.valueOf(i));
assertThat(s1).isSameAs(s2);
}
#Test
public void shouldCacheTwoValues() {
// given
int i1 = 1;
int i2 = 2;
int expectedCacheSize = 2;
// when
String s1 = intStringCache.getStringFor(i1);
String s2 = intStringCache.getStringFor(i2);
// then
assertThat(intStringCache.getSize()).isEqualTo(expectedCacheSize);
assertThat(s1).isSameAs(intStringCache.getStringFor(i1));
assertThat(s2).isSameAs(intStringCache.getStringFor(i2));
}
}
Note:
assertThat(String.valueOf(1)).isSameAs(String.valueOf(1));
fails.
The fact that the second test passes is interesting as, with the bug, there should be one key in the map that gets updated. This may be explained with hashCode() that could make the same key go into two different buckets inside HashMap. But how is it possible that the same key (even if in two buckets) returns the same two Stings? It seems that even though there is a bug in the code the HashMap does the job correctly.
My Android Java implementation, on the other hand, returns wrong number Strings with this bug at once.
You should consider replacing this entire class with SparseArray or its Support Library equivalent SparseArrayCompat (if you need it on <3.0 devices) as they are specifically designed to map integers to objects in a memory efficient way.

What is the Time Complexity of size() for Sets in Java?

I know, it seems like a stupid question, you would expect that the time complexity of size() on any collection would be O(1) - but I'm finding that an "optimization" in my code which requires a call to size() is actually slowing things down.
So, what is the time complexity of size() for Sets in Java?
My code is an implementation of a recursive algorithm to find the maximal cliques in a graph (not important). Basically, the optimization just checks whether two Sets have equal size (these Sets are being constructed either way), and allows for only one more recursive call (stopping the recursion after that) if they are equal in size.
Here is a (simplified) version of my code:
private static void recursivelyFindMaximalCliques(Set<Integer> subGraph, Set<Integer> candidates, Set<Integer> notCandidates) {
boolean noFurtherCliques = false;
Iterator<Integer> candidateIterator = candidates.iterator();
while (candidateIterator.hasNext()) {
int nextCandidate = candidateIterator.next();
candidateIterator.remove();
subGraph.add(nextCandidate);
Set<Integer> neighbors = getNeighbors(nextCandidate);
Set<Integer> newCandidates = calculateIntersection(candidates, neighbors);
Set<Integer> newNotCandidates = calculateIntersection(notCandidates, neighbors);
//if (newCandidates.size() == candidates.size())
// noFurtherCliques = true;
recursivelyFindMaximalCliques(subGraph, newCandidates, newNotCandidates);
//if (noFurtherCliques)
// return;
subGraph.set.remove(nextCandidate);
notCandidates.set.add(nextCandidate);
}
}
The lines I have commented out are the ones in question - you can see that they check if the sets newCandidates and candidates are the same size, and if they are, the recursion is only allowed to go one level deeper.
When the lines are uncommented, the code runs about 10% slower - this is true whether the sets used are HashSets, TreeSets, or LinkedHashSets. This makes no sense, since those lines ensure that there will be FEWER recursive calls.
The only thing I can assume is that the call to size() on the sets is taking a long time. Does calling size() on Sets in Java take longer than O(1)?
EDIT
Since some people have asked, here is calculateIntersection():
private static IntegerSet calculateIntersection(Set<Integer> setA, Set<Integer> setB) {
if (setA.size() == 0 || setB.size() == 0)
return new Set<Integer>();
Set<Integer> intersection = new Set<Integer>(); //Replace this with TreeSet, HashSet, or LinkedHashSet, whichever is being used
intersection.addAll(setA);
intersection.retainAll(setB);
return intersection;
}
SECOND EDIT
Here is the full code, if you like. I hesitated to post it, since it's long and nasty, but people asked, so here it is:
public class CliqueFindingAlgorithm {
private static class IntegerSet {
public Set<Integer> set = new TreeSet<Integer>(); //Or whatever Set is being used
}
private static ArrayList<IntegerSet> findMaximalCliques(UndirectedGraph graph) {
ArrayList<IntegerSet> cliques = new ArrayList<IntegerSet>();
IntegerSet subGraph = new IntegerSet();
IntegerSet candidates = new IntegerSet();
IntegerSet notCandidates = new IntegerSet();
for (int vertex = 0; vertex < graph.getNumVertices(); vertex++) {
candidates.set.add(vertex);
}
recursivelyFindMaximalCliques(cliques, graph, subGraph, candidates, notCandidates);
return cliques;
}
private static void recursivelyFindMaximalCliques(ArrayList<IntegerSet> cliques, UndirectedGraph graph,
IntegerSet subGraph, IntegerSet candidates, IntegerSet notCandidates) {
boolean noFurtherCliques = false;
Iterator<Integer> candidateIterator = candidates.set.iterator();
while (candidateIterator.hasNext()) {
int nextCandidate = candidateIterator.next();
candidateIterator.remove();
subGraph.set.add(nextCandidate);
IntegerSet neighbors = new IntegerSet();
neighbors.set = graph.getNeighbors(nextCandidate);
IntegerSet newCandidates = calculateIntersection(candidates, neighbors);
IntegerSet newNotCandidates = calculateIntersection(notCandidates, neighbors);
if (newCandidates.set.size() == candidates.set.size())
noFurtherCliques = true;
recursivelyFindMaximalCliques(cliques, graph, subGraph, newCandidates, newNotCandidates);
if (noFurtherCliques)
return;
subGraph.set.remove(nextCandidate);
notCandidates.set.add(nextCandidate);
}
if (notCandidates.set.isEmpty()) {
IntegerSet clique = new IntegerSet();
clique.set.addAll(subGraph.set);
cliques.add(clique);
}
}
private static IntegerSet calculateIntersection(IntegerSet setA, IntegerSet setB) {
if (setA.set.size() == 0 || setB.set.size() == 0)
return new IntegerSet();
IntegerSet intersection = new IntegerSet();
intersection.set.addAll(setA.set);
intersection.set.retainAll(setB.set);
return intersection;
}
}
public class UndirectedGraph {
// ------------------------------ PRIVATE VARIABLES ------------------------------
private ArrayList<TreeMap<Integer, Double>> neighborLists;
private int numEdges;
// ------------------------------ CONSTANTS ------------------------------
// ------------------------------ CONSTRUCTORS ------------------------------
public UndirectedGraph(int numVertices) {
this.neighborLists = new ArrayList<TreeMap<Integer, Double>>(numVertices);
this.numEdges = 0;
for (int i = 0; i < numVertices; i++) {
this.neighborLists.add(new TreeMap<Integer, Double>());
}
}
// ------------------------------ PUBLIC METHODS ------------------------------
public void addEdge(int vertexA, int vertexB, double edgeWeight) {
TreeMap<Integer, Double> vertexANeighbors = this.neighborLists.get(vertexA);
TreeMap<Integer, Double> vertexBNeighbors = this.neighborLists.get(vertexB);
vertexANeighbors.put(vertexB, edgeWeight);
vertexBNeighbors.put(vertexA, edgeWeight);
this.numEdges++;
}
public List<Integer> computeCommonNeighbors(int vertexA, int vertexB) {
List<Integer> commonNeighbors = new ArrayList<Integer>();
Iterator<Integer> iteratorA = this.getNeighbors(vertexA).iterator();
Iterator<Integer> iteratorB = this.getNeighbors(vertexB).iterator();
if (iteratorA.hasNext() && iteratorB.hasNext()) {
int nextNeighborA = iteratorA.next();
int nextNeighborB = iteratorB.next();
while(true) {
if (nextNeighborA == nextNeighborB) {
commonNeighbors.add(nextNeighborA);
if (iteratorA.hasNext() && iteratorB.hasNext()) {
nextNeighborA = iteratorA.next();
nextNeighborB = iteratorB.next();
}
else
break;
}
else if (nextNeighborA < nextNeighborB) {
if (iteratorA.hasNext())
nextNeighborA = iteratorA.next();
else
break;
}
else if (nextNeighborB < nextNeighborA) {
if (iteratorB.hasNext())
nextNeighborB = iteratorB.next();
else
break;
}
}
}
return commonNeighbors;
}
// ------------------------------ PRIVATE METHODS ------------------------------
private class EdgeIterator implements Iterator<int[]> {
private int vertex;
private int[] nextPair;
private Iterator<Integer> neighborIterator;
public EdgeIterator() {
this.vertex = 0;
this.neighborIterator = neighborLists.get(0).keySet().iterator();
this.getNextPair();
}
public boolean hasNext() {
return this.nextPair != null;
}
public int[] next() {
if (this.nextPair == null)
throw new NoSuchElementException();
int[] temp = this.nextPair;
this.getNextPair();
return temp;
}
public void remove() {
throw new UnsupportedOperationException();
}
private void getNextPair() {
this.nextPair = null;
while (this.nextPair == null && this.neighborIterator != null) {
while (this.neighborIterator.hasNext()) {
int neighbor = this.neighborIterator.next();
if (this.vertex <= neighbor) {
this.nextPair = new int[] {vertex, neighbor};
return;
}
}
this.vertex++;
if (this.vertex < getNumVertices())
this.neighborIterator = neighborLists.get(this.vertex).keySet().iterator();
else
this.neighborIterator = null;
}
}
}
// ------------------------------ GETTERS & SETTERS ------------------------------
public int getNumEdges() {
return this.numEdges;
}
public int getNumVertices() {
return this.neighborLists.size();
}
public Double getEdgeWeight(int vertexA, int vertexB) {
return this.neighborLists.get(vertexA).get(vertexB);
}
public Set<Integer> getNeighbors(int vertex) {
return Collections.unmodifiableSet(this.neighborLists.get(vertex).keySet());
}
public Iterator<int[]> getEdgeIterator() {
return new EdgeIterator();
}
}
It depends on the implementation; for example HashSet.size() simply calls size() on its internal hashMap which returns a variable;
//HashSet
public int size() {
return map.size();
}
//Hashmap
public int size() {
return size;
}
It depends on the implementation. For example, consider SortedSet.subSet. That returns a SortedSet<E> (which is therefore a Set<E>) but I certainly wouldn't expect the size() operation on that subset to be O(1).
You haven't said what kind of set you're using, nor exactly what the calculateIntersection methods do - but if they're returning views onto existing sets, I wouldn't be at all surprised to hear that finding the size of that view is expensive.
You've talked about HashSet, TreeSet, and LinkedHashSet, all of which are O(1) for size()... but if the calculateIntersection method originally takes one of those sets and creates a view from it, that could well explain what's going on. It would help if you'd give more details - ideally a short but complete program we could use to reproduce the problem.

remove smallest k elements from hashmap in JAVA

I have a hashmap of objects. Each object has two attributes (let say int length and int weight).
I want to remove k elements with the smallest length.
What is the efficient way of doing this?
Map<K, V> map = new HashMap<>();
...
Set<K> keys = map.keySet();
TreeSet<K> smallest = new TreeSet<>(new Comparator<K>(){
public int compare(K o1, K o2) {
return o1.getLength() - o2.getLength();
}
});
smallest.addAll(keys);
for(int x = 0; x < num; x++) {
keys.remove(smallest.pollFirst());
}
Where K is your key type, V is your value type, and num is the number of elements you wish to remove.
If you are doing this frequently, it might be a good idea to use a TreeMap in the first place.
The easiest, but certainly not the most efficient is to create an instance of a TreeMap with provided Comparator for your type, putAll() elements from your map to the map you just created and remove k-elements with help of keySet(). In the end a TreeMap will not contain k-smallest elements.
You didn't mention if the attribute you discriminate on is part of the key or the value, if it's the key then teh treemap discussed above is applicbale.
Otherwise If you need to do this often I'd be inclined to implement my own map, delegating everything in the map interface to a hashmap (or appropriate structure0. Override the add/remove and if necessary iterator, then use the add/remove to maintain a sorted list of the values.
This obviously assumes the values don't change and is highly coupled to your problem.
Keep in mind that TreeMap sorts by the natural ordering of its keys. Hence you can create a key with comparable based on the length of it's value. For example (Since I am on Lunch the code isn't perfect but should get you to what you need):
package com.trip.test;
import java.util.SortedMap;
import java.util.TreeMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ComparisonTest {
private static Logger logger = LoggerFactory.getLogger(ComparisonTest.class);
private static String[] a = {"1","2","3","4"};
private static String[] b = {"A","B","D"};
private static String[] c = {"1","B","D","1","B","D"};
/**
* #param args
*/
static SortedMap<KeyWithLength, String[]> myMap = new TreeMap<KeyWithLength, String[]>();
static {
myMap.put(new KeyWithLength("a", a.length), a);
myMap.put(new KeyWithLength("b", b.length), b);
myMap.put(new KeyWithLength("c", c.length), c);
}
public static void main(String[] args) {
// print Map
logger.info("Original Map:");
int i = 0;
for (String[] strArray: myMap.values() ){
logger.info(String.format("*** Entry %s: ", i++));
printStrings(strArray);
}
// chop off 2 shortest
chopNShortest(myMap, 2);
// print Map
logger.info("ShortenedMap:");
i = 0;
for (String[] strArray: myMap.values() ){
logger.info(String.format("*** Entry %s: ", i++));
printStrings(strArray);
}
}
static void printStrings(String[] strArray){
StringBuffer buf = new StringBuffer();
for (String str: strArray){
buf.append(String.format("%s, ", str));
}
logger.info(buf.toString());
}
static void chopNShortest(SortedMap<KeyWithLength, String[]> sortedMap, int n) {
// Assuming map is not unmodifiable
if (n <= sortedMap.size()-1){
for (int i = 0; i< n;i++){
sortedMap.remove(sortedMap.firstKey());
}
}
}
}
class KeyWithLength implements Comparable<KeyWithLength> {
private String key;
private Integer length;
public KeyWithLength(String key, int length) {
super();
this.key = key;
this.length = length;
}
public String getKey() {
return key;
}
public int getLength() {
return length;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((key == null) ? 0 : key.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
KeyWithLength other = (KeyWithLength) obj;
if (key == null) {
if (other.key != null)
return false;
} else if (!key.equals(other.key))
return false;
return true;
}
#Override
public int compareTo(KeyWithLength another) {
// TODO Auto-generated method stub
return compare(this.length, another.length);
}
public static int compare(int x, int y) {
return (x < y) ? -1 : ((x == y) ? 0 : 1);
}
}
The output:
Original Map:
*** Entry 0:
A, B, D,
*** Entry 1:
1, 2, 3, 4,
*** Entry 2:
1, B, D, 1, B, D,
ShortenedMap:
*** Entry 0:
1, B, D, 1, B, D,

Categories