/* * Copyright (C) 2021 Katherine Murphy. All rights reserved. Permission is * hereby granted to students registered for University of Washington * CSE 331 for use solely during Summer Quarter 2021 for purposes of * the course. No other use, copying, distribution, or modification * is permitted without prior written consent. Copyrights for * third-party components of this work must be honored. Instructors * interested in reusing these course materials should contact the * author. */ package todo.sec09; import java.util.*; public class SuperGeneBFS { // NOTE TO STUDENTS: This class is a Frankenstein of all of the assignments // that you have implemented up until HW9. As a result, this class does not // represent good coding practices. Classes should have a cohesive purpose // and like methods, should not try to do too much, in order to avoid coupling. // This class was designed the way it was in order to make a demo that did not // reveal too much of the implementation of past assignments to you, the student. // AF (this) = {Connections, Members} where // Connections = Genealogy.Branches // Members = names of people in the family tree or Genealogy.Members // Representation Invariant: // familyTree != null /** * Holds all of the Members and their Connections in a family tree or Genealogy. */ private Genealogy familyTree; /** * @spec.effects Constructs a new SuperGeneBFS, {}. */ public SuperGeneBFS() { familyTree = new Genealogy(); Map> temp = parseData(); // initializes the family tree from the data received by temp for (String parent : temp.keySet()) { if (!familyTree.contains(parent)) { familyTree.addMember(parent); } // loops through each child of the parent and adds a // branch between the parent and the child for (String child : temp.get(parent)) { if (!familyTree.contains(child)) { familyTree.addMember(child); } if (!parent.equals(child)) { familyTree.addBranch(parent, child); } } } checkRep(); } /** * Returns if this contains the specified Member. * * @param name The name of a family member to query. * @return {@literal true} iff the short name provided exists in this family tree. */ public boolean familyMemberExists(String name) { checkRep(); try { return familyTree.contains(name); } finally { checkRep(); } } /** * Returns the shortest path from the start to the destination * via branches in the given family tree. * * @param start name of the family member where the path must start. * @param destination name of the family member where the path must end. * @return the path with the least amount of branches from start to destination, * or null if no such path exists. If there are more than one shortest * path from start to destination, returns the lexicographically least one. * @spec.requires start != null, destination != null * start and destination exist in this as family members. */ public Path findPath(String start, String destination) { checkRep(); assert (start != null) : "start == null"; assert (destination != null) : "destination == null"; assert (familyTree.contains(start)) : "!familyTree.contains(start)"; assert (familyTree.contains(destination)) : "!familyTree.contains(destination)"; List untranslatedPath = getPath(start, destination); if (untranslatedPath != null) { Path temp = new Path(start); // sees if is the case where it is from a member to itself if (!start.equals(destination)) { // the cost of one new connection between family members // represents one new added branch/layer to the family tree int costOfRelationship = 1; // translates to temp for (var b : untranslatedPath) { temp = temp.extend(b.child, costOfRelationship); } } checkRep(); return temp; } checkRep(); return null; } /** * Finds untranslated path for findPath. * * @param start name of the family member where the path must start. * @param destination name of the family member where the path must end. * @return the path with the least amount of branches from start to destination, * or null if no such path exists. If there are more than one shortest * path from start to destination, returns the lexicographically least one. */ private List getPath(String start, String destination) { Queue queue = new LinkedList<>(); // temp is a map from family members to paths // Each key in temp is a visited member // Each value is a path from start to that member // A path is a list of Genealogy.Branch Map> temp = new HashMap<>(); queue.add(start); temp.put(start, new ArrayList<>()); while (!queue.isEmpty()) { String next = queue.remove(); if (next.equals(destination)) { return temp.get(next); } // sort the outgoing branches so that the algorithm will pick the // lexicographically least family tree Set outgoingEdges = familyTree.getOutgoingBranches(next); TreeSet sortedEdges = new TreeSet<>(Comparator.comparing(b -> (b.child))); sortedEdges.addAll(outgoingEdges); for (Genealogy.Branch branch : sortedEdges) { if (!temp.containsKey(branch.child)) { // branch.child is not yet visited List p = temp.get(next); List p_prime = new ArrayList<>(p); p_prime.add(branch); temp.put(branch.child, p_prime); queue.add(branch.child); } } } return null; } /** * Reads the dataset. Each line contains a parent name and a child name, * separated by a comma. * * @return a Map of Strings to Sets of Strings. Each of the Map's keys represent * a parent in the family tree and the key's corresponding value represent all * of the parent's children. */ public Map> parseData() { checkRep(); List lines = parseDataset(); // parents as keys, children as values Map> temp = new HashMap<>(); // splits each line into its individual parts and collects the data for (String line : lines) { String[] splitLine = line.split(","); String parent = splitLine[0]; String child = splitLine[1]; if (!temp.containsKey(parent)) { temp.put(parent, new HashSet<>()); } temp.get(parent).add(child); } checkRep(); return temp; } /** * Reads all lines contained within the hardcoded dataset. * * @return A new {@link List} containing all lines in the dataset. */ private List parseDataset() { checkRep(); List lines = new ArrayList<>(); // Yes, these are presidents. No, I know we're not in monarchy :) lines.add("Washington,Adams"); // branch #1 lines.add("Adams,Jefferson"); // branch #2 lines.add("Jefferson,Madison"); // branch #3 lines.add("Madison,Monroe"); // branch #4 lines.add("Monroe,Quincy-Adams"); // branch #5 lines.add("Quincy-Adams,Jackson"); // branch #6 lines.add("Jackson,Buren"); // branch #7 lines.add("Buren,Harrison"); // branch #8 lines.add("Harrison,Tyler"); // branch #9 lines.add("Tyler,Polk"); // branch #10 checkRep(); return lines; } /** * Throws an exception if the representation invariant is violated. */ private void checkRep() { assert (familyTree != null) : "familyTree == null"; } }