subscribers = new ArrayList<>();
+ private Context context;
+ private final String LOG_KEY = "NetCon";
+
+ //region JoinMethods
+ /**
+ * Sends to a given valid peer a request to join his network.
+ * It also sends the current network state.
+ * @param peer The peer to send the message to.
+ * @throws IllegalArgumentException If the peer is invalid or null
+ */
+ public void askToJoin(SMSPeer peer){
+ if(peer == null || !peer.isValid()) throw new IllegalArgumentException();
+ String textRequest = RequestType.JoinPermission.ordinal() + " " + peersInNetwork();
+ SMSMessage message = new SMSMessage(peer, textRequest);
+
+ SMSManager.getInstance(context).sendMessage(message);
+ }
+
+ /**
+ * Sends a given SMSPeer a request to Join this network,
+ * It also sends the current network state
+ * @param peer The peer to send the message to.
+ * @throws IllegalArgumentException If the peer is invalid or null
+ */
+ public void inviteToJoin(SMSPeer peer){
+ /*
+ A net request connection is one of two types:
+ 1. A asks B to join B's network
+ 2. A invites B to join A's network
+
+ Since either case is valid they can be supported in a simple way:
+ being the same type of request, so an invite to join a network is the same as
+ asking to enter the other's network, so I simply call the other function
+ */
+ askToJoin(peer);
+ }
+
+ /**
+ * Accepts a request to Join this network, then sends the peer an update on the net
+ * @param linkPeer The Peer asking to join this network, working as a link between 2 nets now joined
+ * @param text The message received with the sender's network state
+ */
+ void acceptJoin(SMSPeer linkPeer, String text){
+ String[] newPeersOnNet = text.split(" ");
+ SMSPeer[] oldPeersInNet = subscribers.toArray(new SMSPeer[0]);
+ //add new peers to my net
+ addToNet(text);
+ addToNet(oldPeersInNet);
+ //notify new peers of my old peers
+ for(String newPeerAddress: newPeersOnNet){
+ Log.d(LOG_KEY, "New Peer: " + newPeerAddress);
+ SMSPeer newPeer = new SMSPeer(newPeerAddress);
+ SMSManager.getInstance(context).sendMessage(new SMSMessage(newPeer, RequestType.AddPeers.ordinal() + " " + oldPeersInNet));
+ }
+ //notify my old peers about the new ones
+ for(SMSPeer oldPeer : oldPeersInNet){
+ Log.d(LOG_KEY, "Old Peer: " + oldPeer.getAddress());
+ SMSManager.getInstance(context).sendMessage(new SMSMessage(oldPeer, RequestType.AddPeers.ordinal() + " " + text));
+ }
+ }
+
+ //endregion
+
+ //region LeaveMethods
+ /**
+ * Sends to a given valid peer a request to leave his network
+ * @param peer The peer to send a message to.
+ * @author Edoardo Raimondi
+ */
+ public void askToLeave(SMSPeer peer){
+ if(peer == null || !peer.isValid()) throw new IllegalArgumentException();
+ SMSMessage message = new SMSMessage(peer, RequestType.LeavePermission.ordinal()+"");
+ SMSManager.getInstance(context).sendMessage(message);
+ }
+
+ /**
+ * Sends to the network a notification that a given valid SMSPeer exited the network
+ * @param peer The Peer who just left this network
+ */
+ void acceptLeave(SMSPeer peer){
+ //remove the peer
+ removeFromNet(peer.getAddress());
+ //notify my old peers about the exit
+ for(SMSPeer oldPeer : subscribers){
+ Log.d(LOG_KEY, "Old Peer: " + oldPeer.getAddress());
+ SMSManager.getInstance(context).sendMessage(new SMSMessage(oldPeer, RequestType.RemovePeers.ordinal() + " " + peer.getAddress()));
+ }
+ }
+
+ //endregion
+
+ /**
+ * Returns a space separated String with all the Peers in my Network
+ */
+ private String peersInNetwork(){
+ String netPeers = "";
+ for(SMSPeer netPeer : subscribers){
+ netPeers = netPeers.concat(netPeer.getAddress()).concat(" ");
+ }
+ return netPeers;
+ }
+
+ /**
+ * Sends to a valid peer a ping to notify that the user is still online
+ * @param peer The peer to send the ping to
+ * @throws IllegalArgumentException If the peer is null or not valid
+ */
+ public void sendPing(SMSPeer peer){
+ if(peer == null || !peer.isValid()) throw new IllegalArgumentException();
+ SMSMessage message = new SMSMessage(peer, RequestType.Ping.ordinal()+"");
+ SMSManager.getInstance(context).sendMessage(message);
+ }
+
+ /**
+ * Function called when a Ping is received from an SMSPeer,
+ * Resets the Ping timer before considering that peer offline
+ * @param peer The peer who sent the ping
+ */
+ void incomingPing(SMSPeer peer){
+ //TODO: implement this (using timers?)
+ }
+
+ //region addToNet
+ /**
+ * Adds a given String list of peers (separated by a space) to the current network
+ * @param peersToAdd The peers to add to the net
+ * @throws IllegalArgumentException If at least one of the peers is invalid
+ * or if the string is empty or null
+ */
+ public void addToNet(String peersToAdd){
+ if(peersToAdd == null || peersToAdd.equals("")) throw new IllegalArgumentException();
+ String[] peers = peersToAdd.split(" ");
+ for(String peer : peers){
+ addToNet(new SMSPeer(peer));
+ }
+ }
+
+ /**
+ * Adds a given valid peer to the network, if the SMSPeer is already
+ * present, it's not added again
+ * @param peer The SMSPeer to add to the net
+ * @throws IllegalArgumentException If the peer is null or invalid
+ */
+ public void addToNet(SMSPeer peer){
+ if(peer == null || !peer.isValid()) throw new IllegalArgumentException();
+ if(!subscribers.contains(peer)) subscribers.add(peer);
+ }
+
+ /**
+ * Adds a given array of valid peers to the network
+ * @param peers the SMSPeer array to add to the net
+ * @throws IllegalArgumentException If at least one peer is null or invalid,
+ * or if the array is null
+ */
+ public void addToNet(SMSPeer[] peers){
+ if(peers == null) throw new IllegalArgumentException();
+ for(SMSPeer peer: peers) addToNet(peer);
+ }
+ //endregion
+
+ //region removeFromNet
+ /**
+ * Removes a given String list of peers from the current network
+ * @param peersInNet a space separated string of valid SMSPeers
+ * @throws IllegalArgumentException if peersInNet is null or if at least
+ * one SMSPeer is null or invalid
+ */
+ public void removeFromNet(String peersInNet){
+ if(peersInNet == null) throw new IllegalArgumentException();
+ Log.d(LOG_KEY, "Removing these Peers: " + peersInNet);
+ String[] peers = peersInNet.split(" ");
+ for(String peer : peers){
+ removeFromNet(new SMSPeer(peer));
+ }
+ }
+
+ /**
+ * Removes a given SMSPeer from the current network
+ * @param peer The SMSPeer to remove from the net
+ * @throws IllegalArgumentException if peer is null or invalid
+ */
+ public void removeFromNet(SMSPeer peer){
+ if(peer == null || !peer.isValid()) throw new IllegalArgumentException();
+ Log.d(LOG_KEY, "Removing this Peer: " + peer);
+ subscribers.remove(peer);
+ }
+
+ /**
+ * Removes an array of valid SMSPeers from the current network
+ * @param peers The list of Peers to remove
+ * @throws IllegalArgumentException if peers is null, or if at least one SMSPeer
+ * is null or invalid
+ */
+ public void removeFromNet(SMSPeer[] peers){
+ if(peers == null) throw new IllegalArgumentException();
+ for (SMSPeer peer : peers) removeFromNet(peer);
+ }
+ //endregion
+
+ //region helperMethods
+ /**
+ * Removes every online peer from the net
+ * N.B. it doesn't notify anyone about the net, just clears it
+ */
+ public void clearNet(){
+ subscribers.clear();
+ }
+
+ /**
+ * Returns true if there is no online Peer in the net
+ */
+ public boolean isNetEmpty(){
+ return subscribers.isEmpty();
+ }
+
+ /**
+ * Returns how many SMSPeers are currently online
+ */
+ public int networkSize(){
+ return subscribers.size();
+ }
+
+ /**
+ * Returns an array of currently online SMSPeers
+ */
+ public SMSPeer[] getOnlinePeers(){
+ return subscribers.toArray(new SMSPeer[0]);
+ }
+
+ //endregion
+
+ /**
+ * Updates the current Network State given a peer in the network to update and it's resources
+ */
+ public void updateNet(String peer, SMSResource[] resources){
+ Log.d(LOG_KEY, "Updating this Peer: " + peer);
+ SMSPeer peerToUpdate = new SMSPeer(peer);
+ subscribers.remove(peerToUpdate);
+ subscribers.add(peerToUpdate);
+ }
+}
diff --git a/webdictionary/src/main/java/com/example/webdictionary/NetworkDictionary.java b/webdictionary/src/main/java/com/example/webdictionary/NetworkDictionary.java
new file mode 100644
index 0000000..1ccde20
--- /dev/null
+++ b/webdictionary/src/main/java/com/example/webdictionary/NetworkDictionary.java
@@ -0,0 +1,40 @@
+package com.example.webdictionary;
+
+
+import com.eis0.smslibrary.Peer;
+
+/**
+ * Extend this class to extend your type of NetworkDictionary,
+ * Simply instantiate this to use it
+ * @author Marco Cognolato
+ */
+interface NetworkDictionary {
+
+
+ /**
+ * Returns the list of resources of a given peer if present, else returns null
+ */
+ V getResource(K key);
+
+ /**
+ * Returns the list of Peers currently on the network dictionary
+ */
+ K[] getAvailableKeys();
+
+ /**
+ * Returns the list of resources currently on the network dictionary
+ */
+ V[] getAvailableResources();
+
+ /**
+ * Adds a valid Peer-Resources[] couple to the network dictionary
+ * @param peer A peer to add to the dictionary
+ * @param resource A list of Resources to add to the dictionary
+ */
+ void add(K peer, V resource);
+
+ /**
+ * Removes a given valid Peer (and all its Resources) from the network dictionary
+ */
+ void remove(K peer);
+}
diff --git a/webdictionary/src/main/java/com/example/webdictionary/NetworkListener.java b/webdictionary/src/main/java/com/example/webdictionary/NetworkListener.java
new file mode 100644
index 0000000..dbf6f7f
--- /dev/null
+++ b/webdictionary/src/main/java/com/example/webdictionary/NetworkListener.java
@@ -0,0 +1,55 @@
+package com.example.webdictionary;
+
+import android.util.Log;
+
+import com.eis0.smslibrary.ReceivedMessageListener;
+import com.eis0.smslibrary.SMSMessage;
+import com.eis0.smslibrary.SMSPeer;
+
+/**
+ * Network listener listening for incoming Network-related Events
+ */
+class NetworkListener implements ReceivedMessageListener {
+ private NetworkConnection net;
+ private final String LOG_KEY = "NetListener";
+ NetworkListener(NetworkConnection net){
+ this.net = net;
+ }
+
+ @Override
+ public void onMessageReceived(SMSMessage message) {
+ String text = message.getData();
+ SMSPeer peer = message.getPeer();
+ //if I'm using simulators I only need to get the last 4 digits of the number
+ if(peer.toString().contains("+1555521")){
+ peer = new SMSPeer(peer.toString().substring(peer.toString().length() - 4));
+ }
+ //convert the code number in the message to the related enum
+ RequestType incomingRequest = RequestType.values()[Integer.parseInt(text.split(" ")[0])];
+ //starts a specific action based on the action received from the other user
+ if(incomingRequest == RequestType.JoinPermission){
+ Log.d(LOG_KEY, "Received Join Permission: accepting...");
+ net.acceptJoin(peer, text.substring(2));
+ }
+ else if(incomingRequest == RequestType.AcceptJoin){
+ Log.d(LOG_KEY, "Received Join Accepted: updating net...");
+ net.addToNet(text.substring(2));
+ }
+ else if(incomingRequest == RequestType.AddPeers){
+ Log.d(LOG_KEY, "Received Update Net Request: updating net...");
+ net.addToNet(text.substring(2));
+ }
+ else if(incomingRequest == RequestType.LeavePermission){
+ Log.d(LOG_KEY, "Received Leave Permission: ...");
+ net.acceptLeave(peer);
+ }
+ else if(incomingRequest == RequestType.AcceptLeave){
+ Log.d(LOG_KEY, "Received Leave Accepted: updating net...");
+ net.removeFromNet(text.substring(2));
+ }
+ else if(incomingRequest == RequestType.Ping){
+ Log.d(LOG_KEY, "Received Ping...");
+ net.incomingPing(peer);
+ }
+ }
+}
\ No newline at end of file
diff --git a/webdictionary/src/main/java/com/example/webdictionary/RequestType.java b/webdictionary/src/main/java/com/example/webdictionary/RequestType.java
new file mode 100644
index 0000000..8c39dc6
--- /dev/null
+++ b/webdictionary/src/main/java/com/example/webdictionary/RequestType.java
@@ -0,0 +1,12 @@
+package com.example.webdictionary;
+
+enum RequestType{
+ JoinPermission,
+ AcceptJoin,
+ AddPeers,
+ RemovePeers,
+ UpdatePeers,
+ LeavePermission,
+ AcceptLeave,
+ Ping
+}
diff --git a/webdictionary/src/main/java/com/example/webdictionary/Resource.java b/webdictionary/src/main/java/com/example/webdictionary/Resource.java
new file mode 100644
index 0000000..bef926b
--- /dev/null
+++ b/webdictionary/src/main/java/com/example/webdictionary/Resource.java
@@ -0,0 +1,12 @@
+package com.example.webdictionary;
+
+/**
+ * @author Marco Cognolato
+ */
+public interface Resource extends Serializable{
+ /**
+ * Returns the Resource data
+ */
+ T getResource();
+
+}
diff --git a/webdictionary/src/main/java/com/example/webdictionary/SMSNetDictionary.java b/webdictionary/src/main/java/com/example/webdictionary/SMSNetDictionary.java
new file mode 100644
index 0000000..f264203
--- /dev/null
+++ b/webdictionary/src/main/java/com/example/webdictionary/SMSNetDictionary.java
@@ -0,0 +1,108 @@
+package com.example.webdictionary;
+
+import android.util.Log;
+
+import com.eis0.smslibrary.Peer;
+import com.eis0.smslibrary.SMSPeer;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author Edoardo Raimondi
+ */
+public class SMSNetDictionary implements NetworkDictionary {
+
+
+ private Map NetDict;
+ private String LOG_KEY = "NET_DICTIONARY";
+
+ public SMSNetDictionary(){
+
+ NetDict = new HashMap<>();
+ }
+
+ /**
+ * If available, it finds the first Key that has a given valid resource, else it returns null
+ * @param resource
+ * @return Key having that resource
+ */
+ public SerializableObject findKeyWithResource(SerializableObject resource) {
+ for (Map.Entry entry : NetDict.entrySet())
+ {
+ if(entry.getValue().equals(resource)){
+ return entry.getKey();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * If present, it returns the given key resource, else it returns null
+ * @param key having resources we want
+ * @return key's resource
+ */
+ public SerializableObject getResource(SerializableObject key){
+ try{
+ return NetDict.get(key);
+ }
+ catch(Exception e){
+ Log.i(LOG_KEY, "User not present.");
+ return null;
+ }
+ }
+
+ /**
+ * @return List of Keys on the network dictionary
+ */
+ public SerializableObject[] getAvailableKeys(){
+ SerializableObject[] allAvailablePeers = new SerializableObject[NetDict.size()];
+ int i = 0;
+ for (Map.Entry entry : NetDict.entrySet()){
+ //Dictionary scanner
+ allAvailablePeers[i++] = entry.getKey();
+ }
+ return allAvailablePeers;
+ }
+
+ /**
+ * @return List of resources currently on the network dictionary
+ */
+ public SerializableObject[] getAvailableResources(){
+ SerializableObject[] allAvailableResources = new SerializableObject[1]; //array to return
+ int index = 0;
+ for (Map.Entry entry : NetDict.entrySet()) //Dictionary scanner
+ {
+ allAvailableResources[index++] = entry.getValue();
+ }
+ // Needs 24 seconds working with 50000 elements. Is there a better way?
+ return allAvailableResources;
+
+ }
+
+
+ /**
+ * Adds a valid Key-Resource couple to the network dictionary.
+ * If already presents, change the key resource
+ * @param key A key to add to the dictionary
+ * @param resource A Resource to add to the dictionary
+ */
+ public void add(SerializableObject key, SerializableObject resource){
+ if(!NetDict.containsKey(key)) NetDict.put(key, resource);
+ else { //associate the new resource
+ NetDict.remove(key);
+ NetDict.put(key, resource); //didn't find a more elegant way
+ }
+ }
+
+ /**
+ * Removes a given valid Key (and its Resource) from the network dictionary
+ * @param key to remove
+ */
+ public void remove(SerializableObject key) {
+ NetDict.remove(key);
+ }
+
+}
+
diff --git a/webdictionary/src/main/java/com/example/webdictionary/SMSNetDictionarySupport.java b/webdictionary/src/main/java/com/example/webdictionary/SMSNetDictionarySupport.java
new file mode 100644
index 0000000..d22294a
--- /dev/null
+++ b/webdictionary/src/main/java/com/example/webdictionary/SMSNetDictionarySupport.java
@@ -0,0 +1,41 @@
+package com.example.webdictionary;
+
+import java.util.Arrays;
+
+/**
+ * This class contains support methods for
+ * SMSNetDictionary
+ * @author Edoardo Raimondi
+ */
+
+
+public class SMSNetDictionarySupport {
+ /**
+ * Doubles array size
+ * @param array to expand
+ * @return Same array with double size
+ */
+ protected static SMSResource[] doubleArraySize(SMSResource[] array) {
+ return java.util.Arrays.copyOf(array, array.length * 2);
+ }
+ /**
+ * Concatenates two arrays. If only one of them is null the other is returned, if both
+ * are null, null is returned
+ * @param array1 to concatenates with array2
+ * @param array2 to concatenates with array1
+ * @return Array containing all the elements of param's arrays; null if both arrays are null,
+ * the other array if only one is null
+ */
+ protected static SMSResource[] concatAll(SMSResource[] array1, SMSResource[] array2) {
+ if(array1 == null && array2 == null) return null;
+ if(array1 == null) return array2;
+ if(array2 == null) return array1;
+ int totalLength = array1.length + array2.length;
+ SMSResource[] result = Arrays.copyOf(array1, totalLength);
+
+ int offset = array1.length;
+
+ System.arraycopy(array2, 0, result, offset, array2.length);
+ return result;
+ }
+}
diff --git a/webdictionary/src/main/java/com/example/webdictionary/Serializable.java b/webdictionary/src/main/java/com/example/webdictionary/Serializable.java
new file mode 100644
index 0000000..300aa94
--- /dev/null
+++ b/webdictionary/src/main/java/com/example/webdictionary/Serializable.java
@@ -0,0 +1,16 @@
+package com.example.webdictionary;
+
+/**
+ * Common structure of a Serializable object
+ * An object is defined as "Serializable" if it can be
+ * converted to a given type T that identifies that object
+ * @author Marco Cognolato
+ */
+public interface Serializable {
+ /**
+ * A serializable object must be serialized,
+ * meaning that it must be converted to a type T identifying that object
+ * @return A type T object that identifies the object serialized
+ */
+ T serialize();
+}
diff --git a/webdictionary/src/main/java/com/example/webdictionary/SerializableObject.java b/webdictionary/src/main/java/com/example/webdictionary/SerializableObject.java
new file mode 100644
index 0000000..cc9941e
--- /dev/null
+++ b/webdictionary/src/main/java/com/example/webdictionary/SerializableObject.java
@@ -0,0 +1,32 @@
+package com.example.webdictionary;
+
+
+import androidx.annotation.NonNull;
+
+/**
+ * A resource key or value has to be represented by a string in order to be sent by a sms.
+ * Every user-defined key or value extends this class.
+ * That force the user to override equals() and toString().
+ * @author Edoardo Raimondi
+ */
+public abstract class SerializableObject{
+
+ /**
+ * Mandatory override for equals()
+ *
+ * @param toCompare object to compare
+ * @return true objects are the same
+ */
+ public abstract boolean equals(Object toCompare);
+
+ /**
+ * Mandatory override for toString()
+ *
+ * @return a string representing the state of the object
+ */
+
+ @NonNull
+ public abstract String toString();
+
+}
+
diff --git a/webdictionary/src/main/res/values/strings.xml b/webdictionary/src/main/res/values/strings.xml
new file mode 100644
index 0000000..7a1f7f8
--- /dev/null
+++ b/webdictionary/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ WebDictionary
+
diff --git a/webdictionary/src/test/java/com/example/webdictionary/SMSNet_Tests.java b/webdictionary/src/test/java/com/example/webdictionary/SMSNet_Tests.java
new file mode 100644
index 0000000..e9b371e
--- /dev/null
+++ b/webdictionary/src/test/java/com/example/webdictionary/SMSNet_Tests.java
@@ -0,0 +1,127 @@
+package com.example.webdictionary;
+
+import com.eis0.smslibrary.SMSPeer;
+
+import org.junit.Test;
+
+import java.util.HashMap;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @author Edoardo Raimondi
+ * @see Testing documentation
+ */
+public class SMSNet_Tests {
+ @Test
+ public void Instantiation_noErrors() {
+ SMSNetDictionary net = new SMSNetDictionary();
+ }
+
+ //SerializableObject key implementation
+ public class SerializableObjectTestKey extends SerializableObject {
+ public int key;
+
+ public SerializableObjectTestKey() {
+ key = 1;
+ }
+
+ @Override
+ public String toString() {
+ return "uno";
+ }
+
+ @Override
+ public boolean equals(Object toCompare) {
+ if((toCompare == null) || (toCompare.getClass() != this.getClass())) {
+ return false;
+ }
+ SerializableObject other = (SerializableObject)toCompare;
+ return other.toString().equals(this.toString());
+ }
+ }
+ //SerializableObject Value implementation
+ public class SerializableObjectTestValue extends SerializableObject {
+ public String value;
+
+ public SerializableObjectTestValue() {
+ value = "test";
+ }
+
+ @Override
+ public String toString() {
+ return value;
+ }
+
+ @Override
+ public boolean equals(Object toCompare) {
+ if ((toCompare == null) || (toCompare.getClass() != this.getClass())) {
+ return false;
+ }
+ SerializableObject other = (SerializableObject) toCompare;
+ return other.toString().equals(this.toString());
+ }
+ }
+
+ //creation of the the keys
+ SerializableObjectTestKey k1 = new SerializableObjectTestKey();
+ SerializableObjectTestKey k2 = new SerializableObjectTestKey();
+
+ //creation of the values
+ SerializableObjectTestValue v1 = new SerializableObjectTestValue();
+ SerializableObjectTestValue v2 = new SerializableObjectTestValue();
+ @Test
+ public void addKey_CheckIfAdded() {
+ SMSNetDictionary net = new SMSNetDictionary();
+ net.add(k1, v2);
+ assertEquals(net.getAvailableKeys()[0], k1);
+ }
+
+ @Test
+ public void addKey_NoResource_CheckIfAdded() {
+ SMSNetDictionary net = new SMSNetDictionary();
+ net.add(k1, null);
+ assertEquals(net.getAvailableKeys()[0], k1);
+ }
+
+ @Test
+ public void addResource_CheckIfAdded() {
+ SMSNetDictionary net = new SMSNetDictionary();
+ net.add(k1, v2);
+ assertEquals(net.getResource(k1), v2);
+ }
+
+ @Test
+ public void removeKey_CheckNoPeer() {
+ SMSNetDictionary net = new SMSNetDictionary();
+ net.add(k2, v1);
+ net.remove(k2);
+ assertEquals(net.getAvailableKeys().length, 0);
+ }
+
+ @Test
+ public void removeKey_CheckNoResources() {
+ SMSNetDictionary net = new SMSNetDictionary();
+ net.add(k1, v1);
+ net.remove(k1);
+ assertEquals(net.getResource(k1), null);
+ }
+
+
+ @Test
+ public void addMultipleKeys(){
+ SMSNetDictionary net = new SMSNetDictionary();
+ net.add(k1, v1);
+ net.add(k2, v2);
+ SerializableObject[] keys = net.getAvailableKeys();
+ assertEquals(keys.length, 2);
+ }
+
+
+
+}
\ No newline at end of file