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'; ...@@ -15,3 +15,4 @@ part 'symbol.dart';
part 'row.dart'; part 'row.dart';
part 'utils.dart'; part 'utils.dart';
part 'result.dart'; part 'result.dart';
part 'parser_exception.dart';
...@@ -7,6 +7,8 @@ part of cassowary; ...@@ -7,6 +7,8 @@ part of cassowary;
class ConstantMember extends EquationMember { class ConstantMember extends EquationMember {
double value = 0.0; double value = 0.0;
bool get isConstant => true;
ConstantMember(this.value); ConstantMember(this.value);
Expression asExpression() => new Expression([], this.value); Expression asExpression() => new Expression([], this.value);
......
...@@ -7,6 +7,10 @@ part of cassowary; ...@@ -7,6 +7,10 @@ part of cassowary;
abstract class EquationMember { abstract class EquationMember {
Expression asExpression(); Expression asExpression();
bool get isConstant;
double get value;
Constraint operator >=(EquationMember m) => asExpression() >= m; Constraint operator >=(EquationMember m) => asExpression() >= m;
Constraint operator <=(EquationMember m) => asExpression() <= m; Constraint operator <=(EquationMember m) => asExpression() <= m;
...@@ -17,7 +21,7 @@ abstract class EquationMember { ...@@ -17,7 +21,7 @@ abstract class EquationMember {
Expression operator -(EquationMember m) => asExpression() - m; 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 { ...@@ -8,6 +8,9 @@ class Expression extends EquationMember {
final List<Term> terms; final List<Term> terms;
final double constant; final double constant;
bool get isConstant => terms.length == 0;
double get value => terms.fold(constant, (value, term) => value + term.value); double get value => terms.fold(constant, (value, term) => value + term.value);
Expression(this.terms, this.constant); Expression(this.terms, this.constant);
...@@ -105,15 +108,51 @@ class Expression extends EquationMember { ...@@ -105,15 +108,51 @@ class Expression extends EquationMember {
return null; return null;
} }
EquationMember operator *(double m) { EquationMember _applyMultiplicand(double m) {
var newTerms = terms.fold(new List<Term>(), (list, term) => list var newTerms = terms.fold(new List<Term>(), (list, term) => list
..add(new Term(term.variable, term.coefficient * m))); ..add(new Term(term.variable, term.coefficient * m)));
return new Expression(newTerms, constant * m); return new Expression(newTerms, constant * m);
} }
EquationMember operator /(double m) { _Pair<Expression, double> _findMulitplierAndMultiplicand(EquationMember m) {
var newTerms = terms.fold(new List<Term>(), (list, term) => list // At least on of the the two members must be constant for the resulting
..add(new Term(term.variable, term.coefficient / m))); // expression to be linear
return new Expression(newTerms, constant / m);
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; ...@@ -7,6 +7,9 @@ part of cassowary;
class Term extends EquationMember { class Term extends EquationMember {
final Variable variable; final Variable variable;
final double coefficient; final double coefficient;
bool get isConstant => false;
double get value => coefficient * variable.value; double get value => coefficient * variable.value;
Term(this.variable, this.coefficient); Term(this.variable, this.coefficient);
......
...@@ -8,5 +8,7 @@ class Variable extends EquationMember { ...@@ -8,5 +8,7 @@ class Variable extends EquationMember {
double value = 0.0; double value = 0.0;
Variable(this.value); Variable(this.value);
bool get isConstant => false;
Expression asExpression() => new Expression([new Term(this, 1.0)], 0.0); Expression asExpression() => new Expression([new Term(this, 1.0)], 0.0);
} }
...@@ -204,37 +204,37 @@ void main() { ...@@ -204,37 +204,37 @@ void main() {
test('simple_multiplication', () { test('simple_multiplication', () {
// Constant // Constant
var c = CM(20.0); var c = CM(20.0);
expect((c * 2.0).value, 40.0); expect((c * CM(2.0)).value, 40.0);
// Variable // Variable
var v = new Variable(20.0); var v = new Variable(20.0);
expect((v * 2.0).value, 40.0); expect((v * CM(2.0)).value, 40.0);
// Term // Term
var t = new Term(v, 1.0); var t = new Term(v, 1.0);
expect((t * 2.0).value, 40.0); expect((t * CM(2.0)).value, 40.0);
// Expression // Expression
var e = new Expression([t], 0.0); 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', () { test('simple_division', () {
// Constant // Constant
var c = CM(20.0); var c = CM(20.0);
expect((c / 2.0).value, 10.0); expect((c / CM(2.0)).value, 10.0);
// Variable // Variable
var v = new Variable(20.0); var v = new Variable(20.0);
expect((v / 2.0).value, 10.0); expect((v / CM(2.0)).value, 10.0);
// Term // Term
var t = new Term(v, 1.0); var t = new Term(v, 1.0);
expect((t / 2.0).value, 10.0); expect((t / CM(2.0)).value, 10.0);
// Expression // Expression
var e = new Expression([t], 0.0); 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 // TODO: Support and test cases where the multipliers and divisors are more
...@@ -355,4 +355,53 @@ void main() { ...@@ -355,4 +355,53 @@ void main() {
expect(s.removeConstraint(c1), Result.success); expect(s.removeConstraint(c1), Result.success);
expect(s.removeConstraint(c1), Result.unknownConstraint); 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