Skip to content

Commit

Permalink
adds hierarchical-parentheses
Browse files Browse the repository at this point in the history
  • Loading branch information
razorcd committed Jun 4, 2018
1 parent 9b35f31 commit 96f845f
Show file tree
Hide file tree
Showing 6 changed files with 464 additions and 0 deletions.
16 changes: 16 additions & 0 deletions hierarchical-parentheses/hierarchical-parentheses.iml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8">
<output url="file://$MODULE_DIR$/target/classes" />
<output-test url="file://$MODULE_DIR$/target/test-classes" />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" scope="TEST" name="Maven: junit:junit:4.11" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.hamcrest:hamcrest-core:1.3" level="project" />
</component>
</module>
63 changes: 63 additions & 0 deletions hierarchical-parentheses/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.demo</groupId>
<artifactId>demo</artifactId>
<version>1.0-SNAPSHOT</version>

<name>demo</name>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>

<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.0.0</version>
</plugin>
<!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.20.1</version>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
8 changes: 8 additions & 0 deletions hierarchical-parentheses/src/main/java/com/demo/App.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.demo;

public class App
{
public static void main( String[] args ) {

}
}
58 changes: 58 additions & 0 deletions hierarchical-parentheses/src/main/java/com/demo/Equation.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.demo;

public class Equation {
private String input;

/**
* Constructor to set an equation as a string. E.g. "{[(2 + 5) * (2 + 1)] * 2 + 1} * 3"
* @param input the equation.
*/
public Equation(String input) {
this.input = input;
}

/**
* Checks if the hierarchy of the parenthesis is valid.
* Validity rules:
* - contains only `(), [], {}` braces
* - all open braces are also closed
* - hierarchy of braces is in the following order: `{[()]}` or `{{{[()]}}}`
*
* @return [boolean] if equation parenthesis are valid.
*/
public boolean isValid() {
Node root = new Node(true);

try {
return input.chars()
.filter(c -> Node.OPEN_CLOSE_BRACES.containsKey((char)c) || Node.OPEN_CLOSE_BRACES.containsValue((char)c))
.mapToObj(v -> new Node((char) v))
.reduce(root, (currentNode, node) -> {
if (node.isOpenBrace()) {
if (currentNode.canContain(node)) {
node.setParent(currentNode);
currentNode.getChildren().add(node);
return node;
} else throw new BrokenEquationException(currentNode.getValue()+" can not contain subnode "+node.getValue());
} else { // else is close brace
if (currentNode.isCloseableWith(node) && currentNode.isHierarchicallyComplete())
return currentNode.getParent();
else throw new BrokenEquationException(currentNode.getValue()+" is not closeable with "+node.getValue()+" or node does not have any hierarchical sub brace.");
}
}).isRoot();
} catch (BrokenEquationException ex) {
return false;
}

}

public class BrokenEquationException extends RuntimeException {
public BrokenEquationException() {
super();
}
public BrokenEquationException(String message) {
super(message);
}
}
}

203 changes: 203 additions & 0 deletions hierarchical-parentheses/src/main/java/com/demo/Node.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
package com.demo;

import java.util.*;

public class Node {
/**
* Contains a map of open and corresponding closing braces.
*/
protected static final Map<Character, Character> OPEN_CLOSE_BRACES = new HashMap<>();

/**
* Each brace should have a weight.
* Starting from most inner brace until most exterior brace they should be weighted in order with a step of 1.
*/
private static final Map<Character, Integer> WEIGHT = new HashMap<>();

/**
* The minimum weight of the braces.
*/
private static final int MIN_WEIGHT;

/**
* The maximum weight of the braces.
*/
private static final int MAX_WEIGHT;

/*
* Braces configuration.
*/
static {
OPEN_CLOSE_BRACES.put('(', ')');
OPEN_CLOSE_BRACES.put('[', ']');
OPEN_CLOSE_BRACES.put('{', '}');
// OPEN_CLOSE_BRACES.put('<', '>');

WEIGHT.put('(', 1);
WEIGHT.put('[', 2);
WEIGHT.put('{', 3);
// WEIGHT.put('<', 4);

MIN_WEIGHT = WEIGHT.values().stream().min(Integer::compareTo).orElseThrow(IllegalArgumentException::new);
MAX_WEIGHT = WEIGHT.values().stream().max(Integer::compareTo).orElseThrow(IllegalArgumentException::new);
}

/**
* It is the starting node that does not contain any value.
*/
private boolean isRoot;

/**
* The opening or closing brace of current node.
*/
private Character value;
private Node parent;
private List<Node> children;
// private boolean isClosed; // YAGNI

public Node() {
isRoot = false;
children = new ArrayList<>();
}

public Node(Character value) {
this();
this.value = value;
}

public Node(boolean isRoot) {
this();
this.isRoot = isRoot;
}

public Node(Character value, Node parent) {
this();
this.value = value;
this.parent = parent;
}

/**
* Checks if current node contains a value of an opening brace.
*
* @return [boolean]
*/
public boolean isOpenBrace() {
return OPEN_CLOSE_BRACES.containsKey(value);
}

/**
* Checks if current node contains a value of an closing brace.
*
* @return [boolean]
*/
public boolean isClosedBrace() {
return OPEN_CLOSE_BRACES.containsValue(value);
}

/**
* Checks if node can hierarchically be a subnode of current node.
*
* @param node the new node
* @return [boolean]
*/
public boolean canContain(Node node) {
return isRoot() || (getWeight() > node.getWeight()) || (getWeight()==MAX_WEIGHT && node.getWeight()==MAX_WEIGHT);
}

/**
* If current node is root node.
*
* @return [boolean]
*/
public boolean isRoot() {
return isRoot;
}

/**
* Checks if current node can be closed with specified node.
*
* @param closingNode the node to close current node with
* @return [boolean]
*/
public boolean isCloseableWith(Node closingNode) {
return OPEN_CLOSE_BRACES.get(value).equals(closingNode.getValue());
}

/**
* Checks if current node is hierarchically complete. If it's the lowest weighted or has any direct hierarchical subbraces.
*
* @return [boolean]
*/
public boolean isHierarchicallyComplete() {
return (getWeight()==MIN_WEIGHT) || hasAnyHierarchicalSubBrace();
}

/**
* Checks if current node has any direct hierarchical subbraces.
*
* @return [boolean]
*/
private boolean hasAnyHierarchicalSubBrace() {
return getChildren().stream().anyMatch(childNode -> (childNode.getWeight()+1==getWeight()) || (childNode.getWeight()==MAX_WEIGHT && getWeight()==MAX_WEIGHT));
}

/**
* Returns the weight of the current node from the weight Set.
*
* @return [int]
*/
public int getWeight() {
return WEIGHT.get(value);
}

public Character getValue() {
return value;
}

public void setValue(Character value) {
this.value = value;
}

public Node getParent() {
return parent;
}

public void setParent(Node parent) {
this.parent = parent;
}

public List<Node> getChildren() {
return children;
}

public void setChildren(List<Node> children) {
this.children = children;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Node node = (Node) o;
return isRoot == node.isRoot &&
Objects.equals(value, node.value) &&
Objects.equals(parent, node.parent) &&
Objects.equals(children, node.children);
}

@Override
public int hashCode() {
return Objects.hash(isRoot, value, parent, children);
}

@Override
public String toString() {
final StringBuffer sb = new StringBuffer("Node{");
sb.append("isRoot=").append(isRoot);
sb.append(", value=").append(value);
sb.append(", parent=").append(parent);
sb.append(", children=").append(children);
sb.append('}');
return sb.toString();
}
}
Loading

0 comments on commit 96f845f

Please sign in to comment.