diff --git a/homework-g595-manucharyan/Database.db.mv.db b/homework-g595-manucharyan/Database.db.mv.db
new file mode 100644
index 000000000..ff488170f
Binary files /dev/null and b/homework-g595-manucharyan/Database.db.mv.db differ
diff --git a/homework-g595-manucharyan/pom.xml b/homework-g595-manucharyan/pom.xml
index 48adf6d01..2776356cf 100644
--- a/homework-g595-manucharyan/pom.xml
+++ b/homework-g595-manucharyan/pom.xml
@@ -10,8 +10,13 @@
4.0.0
homework-g595-manucharyan
+ pom
1.0.0
+
+ 1.4.2.RELEASE
+
+
ru.mipt.java2016
@@ -19,6 +24,38 @@
1.0.0
+
+ net.sourceforge.jeval
+ jeval
+ 0.9.4
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+ ${spring.boot.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-jdbc
+ ${spring.boot.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-security
+ ${spring.boot.version}
+
+
+ com.zaxxer
+ HikariCP
+ 2.5.1
+
+
+ com.h2database
+ h2
+ 1.4.193
+
+
ru.mipt.java2016
homework-tests
@@ -26,4 +63,4 @@
test
-
\ No newline at end of file
+
diff --git a/homework-g595-manucharyan/src/main/java/ru/mipt/java2016/homework/g595/manucharyan/task4/BillingDao.java b/homework-g595-manucharyan/src/main/java/ru/mipt/java2016/homework/g595/manucharyan/task4/BillingDao.java
new file mode 100644
index 000000000..999ac4ac2
--- /dev/null
+++ b/homework-g595-manucharyan/src/main/java/ru/mipt/java2016/homework/g595/manucharyan/task4/BillingDao.java
@@ -0,0 +1,63 @@
+package ru.mipt.java2016.homework.g595.manucharyan.task4;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.dao.EmptyResultDataAccessException;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.core.RowMapper;
+import org.springframework.stereotype.Repository;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+import javax.annotation.PostConstruct;
+import javax.sql.DataSource;
+
+
+@Repository
+public class BillingDao {
+ private static final Logger LOG = LoggerFactory.getLogger(BillingDao.class);
+
+ @Autowired
+ private DataSource dataSource;
+
+ private JdbcTemplate jdbcTemplate;
+
+ @PostConstruct
+ public void postConstruct() {
+ jdbcTemplate = new JdbcTemplate(dataSource, false);
+ initSchema();
+ }
+
+ public void initSchema() {
+ LOG.trace("Initializing schema");
+ jdbcTemplate.execute("CREATE SCHEMA IF NOT EXISTS billing");
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS billing.users " +
+ "(username VARCHAR PRIMARY KEY, password VARCHAR, enabled BOOLEAN)");
+ try {
+ jdbcTemplate.update("INSERT INTO billing.users VALUES ('username', 'password', TRUE)");
+ } catch (Exception e) {
+ System.out.print("");
+ }
+ }
+
+
+ public BillingUser loadUser(String username) throws EmptyResultDataAccessException {
+ LOG.trace("Querying for user " + username);
+ return jdbcTemplate.queryForObject(
+ "SELECT username, password, enabled FROM billing.users WHERE username = ?",
+ new Object[]{username},
+ new RowMapper() {
+ @Override
+ public BillingUser mapRow(ResultSet rs, int rowNum) throws SQLException {
+ return new BillingUser(
+ rs.getString("username"),
+ rs.getString("password"),
+ rs.getBoolean("enabled")
+ );
+ }
+ }
+ );
+ }
+}
diff --git a/homework-g595-manucharyan/src/main/java/ru/mipt/java2016/homework/g595/manucharyan/task4/BillingDatabaseConfiguration.java b/homework-g595-manucharyan/src/main/java/ru/mipt/java2016/homework/g595/manucharyan/task4/BillingDatabaseConfiguration.java
new file mode 100644
index 000000000..3a0c0704a
--- /dev/null
+++ b/homework-g595-manucharyan/src/main/java/ru/mipt/java2016/homework/g595/manucharyan/task4/BillingDatabaseConfiguration.java
@@ -0,0 +1,23 @@
+package ru.mipt.java2016.homework.g595.manucharyan.task4;
+
+import com.zaxxer.hikari.HikariConfig;
+import com.zaxxer.hikari.HikariDataSource;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import javax.sql.DataSource;
+
+@Configuration
+public class BillingDatabaseConfiguration {
+ @Bean
+ public DataSource billingDataSource() {
+ HikariConfig config = new HikariConfig();
+ config.setDriverClassName(org.h2.Driver.class.getName());
+ config.setJdbcUrl("jdbc:h2:C:\\Users\\op\\Documents\\GitHub\\mipt-java-2016\\" +
+ "homework-g595-manucharyan\\Database.db");
+ config.setUsername("");
+ config.setPassword("");
+ return new HikariDataSource(config);
+ }
+}
diff --git a/homework-g595-manucharyan/src/main/java/ru/mipt/java2016/homework/g595/manucharyan/task4/BillingUser.java b/homework-g595-manucharyan/src/main/java/ru/mipt/java2016/homework/g595/manucharyan/task4/BillingUser.java
new file mode 100644
index 000000000..5c86b9ba0
--- /dev/null
+++ b/homework-g595-manucharyan/src/main/java/ru/mipt/java2016/homework/g595/manucharyan/task4/BillingUser.java
@@ -0,0 +1,69 @@
+package ru.mipt.java2016.homework.g595.manucharyan.task4;
+
+public class BillingUser {
+ private final String username;
+ private final String password;
+ private final boolean enabled;
+
+ public BillingUser(String username, String password, boolean enabled) {
+ if (username == null) {
+ throw new IllegalArgumentException("Null username is not allowed");
+ }
+ if (password == null) {
+ throw new IllegalArgumentException("Null password is not allowed");
+ }
+ this.username = username;
+ this.password = password;
+ this.enabled = enabled;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ @Override
+ public String toString() {
+ return "BillingUser{" +
+ "username='" + username + '\'' +
+ ", password='" + password + '\'' +
+ ", enabled=" + enabled +
+ '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ BillingUser that = (BillingUser) o;
+
+ if (enabled != that.enabled) {
+ return false;
+ }
+ if (!username.equals(that.username)) {
+ return false;
+ }
+ return password.equals(that.password);
+
+ }
+
+ @Override
+ public int hashCode() {
+ int result = username.hashCode();
+ result = 31 * result + password.hashCode();
+ result = 31 * result + (enabled ? 1 : 0);
+ return result;
+ }
+}
diff --git a/homework-g595-manucharyan/src/main/java/ru/mipt/java2016/homework/g595/manucharyan/task4/CalculatorController.java b/homework-g595-manucharyan/src/main/java/ru/mipt/java2016/homework/g595/manucharyan/task4/CalculatorController.java
new file mode 100644
index 000000000..3df767a7b
--- /dev/null
+++ b/homework-g595-manucharyan/src/main/java/ru/mipt/java2016/homework/g595/manucharyan/task4/CalculatorController.java
@@ -0,0 +1,39 @@
+package ru.mipt.java2016.homework.g595.manucharyan.task4;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+import ru.mipt.java2016.homework.base.task1.Calculator;
+import ru.mipt.java2016.homework.base.task1.ParsingException;
+
+@RestController
+public class CalculatorController {
+ private static final Logger LOG = LoggerFactory.getLogger(CalculatorController.class);
+ @Autowired
+ private Calculator calculator;
+
+ @RequestMapping(path = "/ping", method = RequestMethod.GET, produces = "text/plain")
+ public String echo() {
+ return "OK\n";
+ }
+
+ @RequestMapping(path = "/", method = RequestMethod.GET, produces = "text/html")
+ public String main(@RequestParam(required = false) String name) {
+ if (name == null) {
+ name = "world";
+ }
+ return "" +
+ "FediqApp" +
+ "Hello, " + name + "!
" +
+ "";
+ }
+
+ @RequestMapping(path = "/eval", method = RequestMethod.POST, consumes = "text/plain", produces = "text/plain")
+ public String eval(@RequestBody String expression) throws ParsingException {
+ LOG.debug("Evaluation request: [" + expression + "]");
+ double result = calculator.calculate(expression);
+ LOG.trace("Result: " + result);
+ return Double.toString(result) + "\n";
+ }
+}
diff --git a/homework-g595-manucharyan/src/main/java/ru/mipt/java2016/homework/g595/manucharyan/task4/RESTCalc.java b/homework-g595-manucharyan/src/main/java/ru/mipt/java2016/homework/g595/manucharyan/task4/RESTCalc.java
new file mode 100644
index 000000000..3e6d52f75
--- /dev/null
+++ b/homework-g595-manucharyan/src/main/java/ru/mipt/java2016/homework/g595/manucharyan/task4/RESTCalc.java
@@ -0,0 +1,437 @@
+package ru.mipt.java2016.homework.g595.manucharyan.task4;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Random;
+import java.util.Stack;
+
+import ru.mipt.java2016.homework.base.task1.Calculator;
+import ru.mipt.java2016.homework.base.task1.ParsingException;
+
+/**
+ * Пример реализации калькулятора средствами JEval.
+ *
+ * @author vanderwardan
+ * @since 17.12.16
+ */
+public class RESTCalc implements Calculator {
+
+ private Stack operations = new Stack<>();
+ private Stack stack = new Stack<>(); // contains expression in postfix notation
+ private int pos = 0; //position of current symbol in expression
+
+ //names of predefined functions and their valencies
+ private final Map predefinedFunctions = new HashMap<>();
+ //values of variables
+ private Map variables = new HashMap<>();
+
+ RESTCalc() {
+ predefinedFunctions.put("sin", 1);
+ predefinedFunctions.put("cos", 1);
+ predefinedFunctions.put("tg", 1);
+ predefinedFunctions.put("sqrt", 1);
+ predefinedFunctions.put("pow", 2);
+ predefinedFunctions.put("abs", 1);
+ predefinedFunctions.put("sign", 1);
+ predefinedFunctions.put("log", 1);
+ predefinedFunctions.put("log2", 1);
+ predefinedFunctions.put("rnd", 0);
+ predefinedFunctions.put("max", 2);
+ predefinedFunctions.put("min", 2);
+ }
+
+ private int getPriority(Symbol s) throws ParsingException {
+ switch (s) {
+ case FUNCTION:
+ return 0;
+ case OBRACKET:
+ return 0;
+ case CBRACKET:
+ return 0;
+ case ADD:
+ return 1;
+ case SUB:
+ return 1;
+ case MUL:
+ return 2;
+ case DIV:
+ return 2;
+ case UNOADD:
+ return 3;
+ case UNOSUB:
+ return 3;
+ default:
+ freeResource();
+ throw new ParsingException("wrong");
+ }
+ }
+
+ private static SymbolType getTokenType(Token t) {
+ switch (t.getSymbol()) {
+ case NUMBER:
+ return SymbolType.NUMBER;
+ case ADD:
+ return SymbolType.OPERATOR;
+ case UNOADD:
+ return SymbolType.OPERATOR;
+ case SUB:
+ return SymbolType.OPERATOR;
+ case UNOSUB:
+ return SymbolType.OPERATOR;
+ case MUL:
+ return SymbolType.OPERATOR;
+ case DIV:
+ return SymbolType.OPERATOR;
+ case OBRACKET:
+ return SymbolType.BRACKET;
+ case CBRACKET:
+ return SymbolType.BRACKET;
+ case VARIABLE:
+ return SymbolType.VARIABLE;
+ case FUNCTION:
+ return SymbolType.FUNCTION;
+ case SPACE:
+ return SymbolType.SPACE;
+ default:
+ return SymbolType.NONE;
+ }
+ }
+
+ //is this symbol right-Associativity
+ private static boolean isRightAssociative(Symbol s) {
+ //in future, here can be added some right-ass operations =)
+ switch (s) {
+ default:
+ return false;
+ }
+ }
+
+ @Override
+ public double calculate(String expression) throws ParsingException {
+ try {
+ if (expression == null) {
+ throw new ParsingException("wrong");
+ }
+
+ //parse expresion and execute it it at the same time
+ //Really, why should we make reverse polish notation and only than calculate it?
+ //We can do it both!
+ while (pos < expression.length()) {
+ Token t = parseString(expression); //current symbol
+
+ switch (getTokenType(t)) {
+ case NUMBER:
+ stack.push(t.getValue());
+ break;
+ case OPERATOR:
+ pushOperationInStack(t);
+ break;
+ case FUNCTION:
+ operations.push(t);
+ break;
+ case BRACKET:
+ if (t.getSymbol() == Symbol.OBRACKET) { //open bracket
+ operations.push(t);
+ } else if (t.getSymbol() == Symbol.CBRACKET) { //close bracket
+ Token tmp = operations.pop();
+ while (!operations.isEmpty() && tmp.getSymbol() != Symbol.OBRACKET ||
+ tmp.getSymbol() == Symbol.FUNCTION) {
+ calculateOperation(tmp);
+ if (tmp.getSymbol() == Symbol.FUNCTION) {
+ break;
+ }
+ tmp = operations.pop();
+ }
+
+ if (tmp.getSymbol() != Symbol.OBRACKET && tmp.getSymbol() != Symbol.FUNCTION) {
+ throw new ParsingException("wrong");
+ }
+ }
+
+ break;
+ case VARIABLE:
+ if (!variables.containsKey(t.getName())) {
+ throw new ParsingException("no such variable");
+ }
+ stack.push(variables.get(t.getName()));
+ break;
+ case SPACE:
+ break;
+ default:
+ break;
+ }
+
+ pos++;
+ }
+
+ //if we parse all expression, and stack isn't empty
+ while (stack.size() != 1 || operations.size() > 0) {
+ if (operations.isEmpty()) {
+ throw new ParsingException("wrong");
+ } else {
+ Token t = operations.pop();
+ calculateOperation(t);
+ }
+ }
+
+ return stack.pop();
+ } finally {
+ freeResource();
+ }
+ }
+
+ private double getNumber(String expression) throws ParsingException {
+ char c = expression.charAt(pos);
+ if (!Character.isDigit(c)) {
+ freeResource();
+ throw new ParsingException("wrong");
+ }
+
+ double res = 0.0;
+
+ //find integer part of number
+ while (Character.isDigit(c)) {
+ res *= 10;
+ res += c - '0';
+ if (pos == expression.length() - 1) {
+ break;
+ }
+ c = expression.charAt(++pos);
+ }
+
+ //find fractional part of number
+ if (c == '.') {
+ double pow = 0.1;
+ if (pos >= expression.length() - 1) {
+ freeResource();
+ throw new ParsingException("wrong");
+ }
+
+ c = expression.charAt(++pos);
+ while (Character.isDigit(c)) {
+ res += (c - '0') * pow;
+ pow *= 0.1;
+ if (pos == expression.length() - 1) {
+ break;
+ }
+ c = expression.charAt(++pos);
+ }
+ }
+
+ //correct pos
+ if (!Character.isDigit(c)) {
+ pos--;
+ }
+
+ return res;
+ }
+
+ private double calculateFunction(String name, double[] operands) {
+ assert (predefinedFunctions.containsKey(name));
+ switch (name) {
+ case "sin":
+ return Math.sin(operands[0]);
+ case "cos":
+ return Math.cos(operands[0]);
+ case "tg":
+ return Math.tan(operands[0]);
+ case "sqrt":
+ return Math.sqrt(operands[0]);
+ case "pow":
+ return Math.pow(operands[0], operands[1]);
+ case "abs":
+ return Math.abs(operands[0]);
+ case "sign":
+ return Math.signum(operands[0]);
+ case "log":
+ return Math.log(operands[0]);
+ case "log2":
+ return Math.log(operands[0]) / Math.log(2);
+ case "rnd":
+ return (new Random()).nextDouble();
+ case "max":
+ return Math.max(operands[0], operands[1]);
+ case "min":
+ return Math.min(operands[0], operands[1]);
+ default:
+ return 0.0;
+ }
+ }
+
+ //return name of variable or function(in this case, without open bracket)
+ private String getName(String expression) throws ParsingException {
+ int oldpos = pos;
+ String name = "";
+ char c = expression.charAt(pos);
+
+ assert (!Character.isDigit(c));
+
+ while (Character.isLetter(c) || c == '_' || Character.isDigit(c)) {
+ name += c;
+ if (pos == expression.length() - 1) {
+ return name;
+ }
+ c = expression.charAt(++pos);
+ }
+ pos--;
+ return name;
+ }
+
+ //execute operation
+ private void calculateOperation(Token t) throws ParsingException {
+ if (stack.size() < t.getValency()) {
+ freeResource();
+ throw new ParsingException("wrong");
+ }
+
+ //get all operands
+ double[] operands = new double[t.getValency()];
+
+ for (int i = 0; i < t.getValency(); ++i) {
+ operands[t.getValency() - i - 1] = stack.pop();
+ }
+
+ //calculate operation
+ switch (t.getSymbol()) {
+ case ADD:
+ stack.push(operands[0] + operands[1]);
+ break;
+ case UNOADD:
+ stack.push(operands[0]);
+ break;
+ case SUB:
+ stack.push(operands[0] - operands[1]);
+ break;
+ case UNOSUB:
+ stack.push(-operands[0]);
+ break;
+ case MUL:
+ stack.push(operands[0] * operands[1]);
+ break;
+ case DIV:
+ stack.push(operands[0] / operands[1]);
+ break;
+ case FUNCTION:
+ stack.push(calculateFunction(t.getName(), operands));
+ break;
+ default:
+ freeResource();
+ throw new ParsingException("wrong");
+ }
+ }
+
+ private Token parseString(String expression) throws ParsingException {
+ char c = expression.charAt(pos);
+ if (Character.isDigit(c)) {
+ return new Token(Symbol.NUMBER, getNumber(expression));
+ }
+ if (c == '+' || c == '-') {
+ char oldc = c;
+ int oldpos = pos;
+ boolean uno = true;
+
+ //try to understand is operation uno or not
+ if (pos > 0) {
+ c = expression.charAt(--pos);
+ while (c == ' ' || c == '\n' || c == '\t') {
+ if (pos > 0) {
+ c = expression.charAt(--pos);
+ } else {
+ break;
+ }
+ }
+ if (c == ')' || c >= '0' && c <= '9') {
+ uno = false;
+ }
+ }
+
+ //return answer
+ pos = oldpos;
+ if (uno) {
+ if (oldc == '+') {
+ return new Token(Symbol.UNOADD);
+ } else {
+ return new Token(Symbol.UNOSUB);
+ }
+ } else {
+ if (oldc == '+') {
+ return new Token(Symbol.ADD);
+ } else {
+ return new Token(Symbol.SUB);
+ }
+ }
+ }
+ if (c == '*') {
+ return new Token(Symbol.MUL);
+ }
+ if (c == '/') {
+ return new Token(Symbol.DIV);
+ }
+ if (c == '(') {
+ return new Token(Symbol.OBRACKET);
+ }
+ if (c == ')') {
+ return new Token(Symbol.CBRACKET);
+ }
+ if (c == '_' || Character.isLetter(c)) {
+ String name = getName(expression);
+ if (pos < expression.length() - 1 && expression.charAt(++pos) == '(') {
+ if (!predefinedFunctions.containsKey(name)) {
+ throw new ParsingException("no such a function");
+ }
+ return new Token(Symbol.FUNCTION, name, predefinedFunctions.get(name));
+ } else {
+ if (!variables.containsKey(name)) {
+ throw new ParsingException("no such a variable");
+ }
+ pos--;
+ return new Token(Symbol.VARIABLE, name, predefinedFunctions.get(name));
+ }
+ }
+ if (c == ' ' || c == '\n' || c == '\t' || c == ',') {
+ return new Token(Symbol.SPACE);
+ }
+
+ //if nothing of that
+ freeResource();
+ throw new ParsingException("wrong");
+ }
+
+ //push a single operation in operation stack ("operations")
+ private void pushOperationInStack(Token t) throws ParsingException {
+ if (operations.isEmpty()) {
+ operations.push(t);
+ } else {
+ Token tmp = operations.lastElement();
+
+ //while it's necessary, pop operation and execute it
+ while ((getPriority(t.getSymbol()) < getPriority(tmp.getSymbol())) && isRightAssociative(t.getSymbol()) ||
+ (getPriority(t.getSymbol()) <= getPriority(tmp.getSymbol())) && !isRightAssociative(t.getSymbol())) {
+
+ operations.pop();
+ calculateOperation(tmp);
+
+ if (operations.isEmpty()) {
+ break;
+ } else {
+ tmp = operations.lastElement();
+ }
+ }
+
+ operations.push(t);
+ }
+ }
+
+ private void freeResource() {
+ stack.clear();
+ operations.clear();
+
+ }
+
+ public enum Symbol {
+ NUMBER, ADD, UNOADD, SUB, UNOSUB, MUL, DIV, OBRACKET,
+ CBRACKET, VARIABLE, FUNCTION, SPACE, NONE
+ }
+
+ public enum SymbolType { OPERATOR, NUMBER, BRACKET, SPACE, VARIABLE, FUNCTION, NONE }
+}
diff --git a/homework-g595-manucharyan/src/main/java/ru/mipt/java2016/homework/g595/manucharyan/task4/SecurityServiceConfiguration.java b/homework-g595-manucharyan/src/main/java/ru/mipt/java2016/homework/g595/manucharyan/task4/SecurityServiceConfiguration.java
new file mode 100644
index 000000000..f0a72ebbe
--- /dev/null
+++ b/homework-g595-manucharyan/src/main/java/ru/mipt/java2016/homework/g595/manucharyan/task4/SecurityServiceConfiguration.java
@@ -0,0 +1,56 @@
+package ru.mipt.java2016.homework.g595.manucharyan.task4;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.dao.EmptyResultDataAccessException;
+import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.core.userdetails.User;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+
+import java.util.Collections;
+
+@Configuration
+@EnableWebSecurity
+public class SecurityServiceConfiguration extends WebSecurityConfigurerAdapter {
+ private static final Logger LOG = LoggerFactory.getLogger(SecurityServiceConfiguration.class);
+
+ @Autowired
+ private BillingDao billingDao;
+
+ @Override
+ protected void configure(HttpSecurity http) throws Exception {
+ LOG.info("Configuring security");
+ http
+ .httpBasic().realmName("Calculator").and()
+ .formLogin().disable()
+ .logout().disable()
+ .csrf().disable()
+ .authorizeRequests()
+ .antMatchers("/eval/**").authenticated()
+ .anyRequest().permitAll();
+ }
+
+ @Autowired
+ public void registerGlobalAuthentication(AuthenticationManagerBuilder auth) throws Exception {
+ LOG.info("Register" +
+ "ing global user details service");
+ auth.userDetailsService(username -> {
+ try {
+ BillingUser user = billingDao.loadUser(username);
+ return new User(
+ user.getUsername(),
+ user.getPassword(),
+ Collections.singletonList(() -> "AUTH")
+ );
+ } catch (EmptyResultDataAccessException e) {
+ LOG.warn("No such user: " + username);
+ throw new UsernameNotFoundException(username);
+ }
+ });
+ }
+}
diff --git a/homework-g595-manucharyan/src/main/java/ru/mipt/java2016/homework/g595/manucharyan/task4/Token.java b/homework-g595-manucharyan/src/main/java/ru/mipt/java2016/homework/g595/manucharyan/task4/Token.java
new file mode 100644
index 000000000..b2fe6ee0d
--- /dev/null
+++ b/homework-g595-manucharyan/src/main/java/ru/mipt/java2016/homework/g595/manucharyan/task4/Token.java
@@ -0,0 +1,64 @@
+package ru.mipt.java2016.homework.g595.manucharyan.task4;
+
+/**
+ * Created by op on 17.12.2016.
+ */
+public class Token {
+
+ public Token(RESTCalc.Symbol symbol) {
+ this.symbol = symbol;
+ valency = getValencyForOperator(symbol);
+ }
+
+ public Token(RESTCalc.Symbol symbol, double value) {
+ this(symbol);
+ this.value = value;
+ }
+
+ public Token(RESTCalc.Symbol symbol, String name, int valency) {
+ this(symbol);
+ this.name = name;
+ this.valency = valency;
+ }
+
+ private double value; // for numbers
+ private String name; // for functions and variables
+ private int valency = 0; // for functions
+ private RESTCalc.Symbol symbol = RESTCalc.Symbol.NONE;
+
+ private int getValencyForOperator(RESTCalc.Symbol s) {
+ switch (s) {
+ case ADD:
+ return 2;
+ case UNOADD:
+ return 1;
+ case SUB:
+ return 2;
+ case UNOSUB:
+ return 1;
+ case MUL:
+ return 2;
+ case DIV:
+ return 2;
+ default:
+ return 0;
+ }
+ }
+
+ //get functions
+ public double getValue() {
+ return value;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public int getValency() {
+ return valency;
+ }
+
+ public RESTCalc.Symbol getSymbol() {
+ return symbol;
+ }
+}
diff --git a/homework-g595-manucharyan/src/main/java/ru/mipt/java2016/homework/g595/manucharyan/task4/VardanApplication.java b/homework-g595-manucharyan/src/main/java/ru/mipt/java2016/homework/g595/manucharyan/task4/VardanApplication.java
new file mode 100644
index 000000000..d028150cb
--- /dev/null
+++ b/homework-g595-manucharyan/src/main/java/ru/mipt/java2016/homework/g595/manucharyan/task4/VardanApplication.java
@@ -0,0 +1,41 @@
+package ru.mipt.java2016.homework.g595.manucharyan.task4;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.Banner;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+import ru.mipt.java2016.homework.base.task1.Calculator;
+
+/**3
+ * curl http://localhost:9001/eval \
+ * -X POST \
+ * -H "Content-Type: text/plain" \
+ * -H "Authorization: Basic $(echo -n "username:password" | base64)" \
+ * --data-raw "44*3+2"
+ */
+@EnableAutoConfiguration
+@Configuration
+@ComponentScan(basePackageClasses = VardanApplication.class)
+public class VardanApplication {
+
+ @Bean
+ public Calculator calculator() {
+ return new RESTCalc();
+ }
+
+ @Bean
+ public EmbeddedServletContainerCustomizer customizer(
+ @Value("${ru.mipt.java2016.homework.g595.manucharyan.task4.httpPort:9001}") int port) {
+ return container -> container.setPort(port);
+ }
+
+ public static void main(String[] args) {
+ SpringApplication application = new SpringApplication(VardanApplication.class);
+ application.setBannerMode(Banner.Mode.OFF);
+ application.run(args);
+ }
+}
diff --git a/homework-g595-manucharyan/src/test/java/ru/mipt/java2016/homework/g595/manucharyan/task4/RESTCalcTest.java b/homework-g595-manucharyan/src/test/java/ru/mipt/java2016/homework/g595/manucharyan/task4/RESTCalcTest.java
new file mode 100644
index 000000000..fb4c26500
--- /dev/null
+++ b/homework-g595-manucharyan/src/test/java/ru/mipt/java2016/homework/g595/manucharyan/task4/RESTCalcTest.java
@@ -0,0 +1,15 @@
+package ru.mipt.java2016.homework.g595.manucharyan.task4;
+
+import ru.mipt.java2016.homework.base.task1.Calculator;
+import ru.mipt.java2016.homework.tests.task1.AbstractCalculatorTest;
+
+/**
+ * @author vanderwardan
+ * @since 19.12.16
+ */
+public class RESTCalcTest extends AbstractCalculatorTest {
+
+ protected Calculator calc() {
+ return new RESTCalc();
+ }
+}