Commit 9ea8abd5 authored by Chinmay Garde's avatar Chinmay Garde

Allow constraint creation from multiplication and division when at least one...

Allow constraint creation from multiplication and division when at least one argument is a constant expression
parent 5137e03c
......@@ -15,3 +15,4 @@ part 'symbol.dart';
part 'row.dart';
part 'utils.dart';
part 'result.dart';
part 'parser_exception.dart';
......@@ -7,6 +7,8 @@ part of cassowary;
class ConstantMember extends EquationMember {
double value = 0.0;
bool get isConstant => true;
ConstantMember(this.value);
Expression asExpression() => new Expression([], this.value);
......
......@@ -7,6 +7,10 @@ part of cassowary;
abstract class EquationMember {
Expression asExpression();
bool get isConstant;
double get value;
Constraint operator >=(EquationMember m) => asExpression() >= m;
Constraint operator <=(EquationMember m) => asExpression() <= m;
......@@ -17,7 +21,7 @@ abstract class EquationMember {
Expression operator -(EquationMember m) => asExpression() - m;
Expression operator *(double m) => asExpression() * m;
Expression operator *(EquationMember m) => asExpression() * m;
Expression operator /(double m) => asExpression() / m;
Expression operator /(EquationMember m) => asExpression() / m;
}
......@@ -8,6 +8,9 @@ class Expression extends EquationMember {
final List<Term> terms;
final double constant;
bool get isConstant => terms.length == 0;
double get value => terms.fold(constant, (value, term) => value + term.value);
Expression(this.terms, this.constant);
......@@ -105,15 +108,51 @@ class Expression extends EquationMember {
return null;
}
EquationMember operator *(double m) {
EquationMember _applyMultiplicand(double m) {
var newTerms = terms.fold(new List<Term>(), (list, term) => list
..add(new Term(term.variable, term.coefficient * m)));
return new Expression(newTerms, constant * m);
}
EquationMember operator /(double m) {
var newTerms = terms.fold(new List<Term>(), (list, term) => list
..add(new Term(term.variable, term.coefficient / m)));
return new Expression(newTerms, constant / m);
_Pair<Expression, double> _findMulitplierAndMultiplicand(EquationMember m) {
// At least on of the the two members must be constant for the resulting
// expression to be linear
if (!this.isConstant && !m.isConstant) {
return null;
}
if (this.isConstant) {
return new _Pair(m.asExpression(), this.value);
}
if (m.isConstant) {
return new _Pair(this.asExpression(), m.value);
}
assert(false);
return null;
}
EquationMember operator *(EquationMember m) {
_Pair<Expression, double> args = _findMulitplierAndMultiplicand(m);
if (args == null) {
throw new ParserException(
"Could not find constant multiplicand or multiplier", [this, m]);
return null;
}
return args.first._applyMultiplicand(args.second);
}
EquationMember operator /(EquationMember m) {
if (!m.isConstant) {
throw new ParserException(
"The divisor was not a constant expression", [this, m]);
return null;
}
return this._applyMultiplicand(1.0 / m.value);
}
}
// Copyright (c) 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
part of cassowary;
class ParserException implements Exception {
final String message;
List<EquationMember> members;
ParserException(this.message, this.members);
String toString() {
if (message == null) return "Error while parsing constraint or expression";
return "Error: '$message' while trying to parse constraint or expression";
}
}
......@@ -7,6 +7,9 @@ part of cassowary;
class Term extends EquationMember {
final Variable variable;
final double coefficient;
bool get isConstant => false;
double get value => coefficient * variable.value;
Term(this.variable, this.coefficient);
......
......@@ -8,5 +8,7 @@ class Variable extends EquationMember {
double value = 0.0;
Variable(this.value);
bool get isConstant => false;
Expression asExpression() => new Expression([new Term(this, 1.0)], 0.0);
}
......@@ -204,37 +204,37 @@ void main() {
test('simple_multiplication', () {
// Constant
var c = CM(20.0);
expect((c * 2.0).value, 40.0);
expect((c * CM(2.0)).value, 40.0);
// Variable
var v = new Variable(20.0);
expect((v * 2.0).value, 40.0);
expect((v * CM(2.0)).value, 40.0);
// Term
var t = new Term(v, 1.0);
expect((t * 2.0).value, 40.0);
expect((t * CM(2.0)).value, 40.0);
// Expression
var e = new Expression([t], 0.0);
expect((e * 2.0).value, 40.0);
expect((e * CM(2.0)).value, 40.0);
});
test('simple_division', () {
// Constant
var c = CM(20.0);
expect((c / 2.0).value, 10.0);
expect((c / CM(2.0)).value, 10.0);
// Variable
var v = new Variable(20.0);
expect((v / 2.0).value, 10.0);
expect((v / CM(2.0)).value, 10.0);
// Term
var t = new Term(v, 1.0);
expect((t / 2.0).value, 10.0);
expect((t / CM(2.0)).value, 10.0);
// Expression
var e = new Expression([t], 0.0);
expect((e / 2.0).value, 10.0);
expect((e / CM(2.0)).value, 10.0);
});
// TODO: Support and test cases where the multipliers and divisors are more
......@@ -355,4 +355,53 @@ void main() {
expect(s.removeConstraint(c1), Result.success);
expect(s.removeConstraint(c1), Result.unknownConstraint);
});
test('test_multiplication_division_override', () {
var c = CM(10.0);
var v = new Variable(c.value);
var t = new Term(v, 1.0);
var e = new Expression([t], 0.0);
// Constant
expect((c * CM(10.0)).value, 100);
// Variable
expect((v * CM(10.0)).value, 100);
// Term
expect((t * CM(10.0)).value, 100);
// Expression
expect((e * CM(10.0)).value, 100);
// Constant
expect((c / CM(10.0)).value, 1);
// Variable
expect((v / CM(10.0)).value, 1);
// Term
expect((t / CM(10.0)).value, 1);
// Expression
expect((e / CM(10.0)).value, 1);
});
test('test_multiplication_division_exceptions', () {
var c = CM(10.0);
var v = new Variable(c.value);
var t = new Term(v, 1.0);
var e = new Expression([t], 0.0);
expect((c * c).value, 100);
expect(() => v * v, throwsA(new isInstanceOf<ParserException>()));
expect(() => v / v, throwsA(new isInstanceOf<ParserException>()));
expect(() => v * t, throwsA(new isInstanceOf<ParserException>()));
expect(() => v / t, throwsA(new isInstanceOf<ParserException>()));
expect(() => v * e, throwsA(new isInstanceOf<ParserException>()));
expect(() => v / e, throwsA(new isInstanceOf<ParserException>()));
expect(() => v * c, returnsNormally);
expect(() => v / c, returnsNormally);
});
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment