| // 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 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); |
| Expression.fromExpression(Expression expr) |
| : this.terms = new List<Term>.from(expr.terms), |
| this.constant = expr.constant; |
| |
| Expression asExpression() => this; |
| |
| Constraint _createConstraint( |
| _EquationMember /* rhs */ value, Relation relation) { |
| if (value is ConstantMember) { |
| return new Constraint( |
| new Expression(new List.from(terms), constant - value.value), |
| relation); |
| } |
| |
| if (value is Param) { |
| var newTerms = new List<Term>.from(terms) |
| ..add(new Term(value.variable, -1.0)); |
| return new Constraint(new Expression(newTerms, constant), relation); |
| } |
| |
| if (value is Term) { |
| var newTerms = new List<Term>.from(terms) |
| ..add(new Term(value.variable, -value.coefficient)); |
| return new Constraint(new Expression(newTerms, constant), relation); |
| } |
| |
| if (value is Expression) { |
| var newTerms = value.terms.fold(new List<Term>.from(terms), |
| (list, t) => list..add(new Term(t.variable, -t.coefficient))); |
| return new Constraint( |
| new Expression(newTerms, constant - value.constant), relation); |
| } |
| |
| assert(false); |
| return null; |
| } |
| |
| Constraint operator >=(_EquationMember value) => |
| _createConstraint(value, Relation.greaterThanOrEqualTo); |
| |
| Constraint operator <=(_EquationMember value) => |
| _createConstraint(value, Relation.lessThanOrEqualTo); |
| |
| operator ==(_EquationMember value) => |
| _createConstraint(value, Relation.equalTo); |
| |
| Expression operator +(_EquationMember m) { |
| if (m is ConstantMember) { |
| return new Expression(new List.from(terms), constant + m.value); |
| } |
| |
| if (m is Param) { |
| return new Expression( |
| new List.from(terms)..add(new Term(m.variable, 1.0)), constant); |
| } |
| |
| if (m is Term) { |
| return new Expression(new List.from(terms)..add(m), constant); |
| } |
| |
| if (m is Expression) { |
| return new Expression( |
| new List.from(terms)..addAll(m.terms), constant + m.constant); |
| } |
| |
| assert(false); |
| return null; |
| } |
| |
| Expression operator -(_EquationMember m) { |
| if (m is ConstantMember) { |
| return new Expression(new List.from(terms), constant - m.value); |
| } |
| |
| if (m is Param) { |
| return new Expression( |
| new List.from(terms)..add(new Term(m.variable, -1.0)), constant); |
| } |
| |
| if (m is Term) { |
| return new Expression(new List.from(terms) |
| ..add(new Term(m.variable, -m.coefficient)), constant); |
| } |
| |
| if (m is Expression) { |
| var copiedTerms = new List<Term>.from(terms); |
| m.terms.forEach( |
| (t) => copiedTerms.add(new Term(t.variable, -t.coefficient))); |
| return new Expression(copiedTerms, constant - m.constant); |
| } |
| |
| assert(false); |
| return null; |
| } |
| |
| _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); |
| } |
| |
| _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); |
| } |
| |
| String toString() { |
| StringBuffer buffer = new StringBuffer(); |
| |
| terms.forEach((t) => buffer.write("${t}")); |
| |
| if (constant != 0.0) { |
| buffer.write(constant.sign > 0.0 ? "+" : "-"); |
| buffer.write(constant.abs()); |
| } |
| |
| return buffer.toString(); |
| } |
| } |