// 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 { Expression(this.terms, this.constant); Expression.fromExpression(Expression expr) : this.terms = new List.from(expr.terms), this.constant = expr.constant; final List terms; final double constant; @override bool get isConstant => terms.length == 0; @override double get value => terms.fold(constant, (double value, Term term) => value + term.value); @override 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) { List newTerms = new List.from(terms) ..add(new Term(value.variable, -1.0)); return new Constraint(new Expression(newTerms, constant), relation); } if (value is Term) { List newTerms = new List.from(terms) ..add(new Term(value.variable, -value.coefficient)); return new Constraint(new Expression(newTerms, constant), relation); } if (value is Expression) { List newTerms = value.terms.fold( new List.from(terms), (List list, Term t) { return list..add(new Term(t.variable, -t.coefficient)); } ); return new Constraint( new Expression(newTerms, constant - value.constant), relation ); } assert(false); return null; } @override Constraint operator >=(_EquationMember value) { return _createConstraint(value, Relation.greaterThanOrEqualTo); } @override Constraint operator <=(_EquationMember value) { return _createConstraint(value, Relation.lessThanOrEqualTo); } @override Constraint equals(_EquationMember value) { return _createConstraint(value, Relation.equalTo); } @override 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; } @override 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) { List copiedTerms = new List.from(terms); for (Term t in m.terms) copiedTerms.add(new Term(t.variable, -t.coefficient)); return new Expression(copiedTerms, constant - m.constant); } assert(false); return null; } _EquationMember _applyMultiplicand(double m) { List newTerms = terms.fold( new List(), (List list, Term term) { return list..add(new Term(term.variable, term.coefficient * m)); } ); return new Expression(newTerms, constant * m); } _Pair _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 args = _findMulitplierAndMultiplicand(m); if (args == null) { throw new ParserException( 'Could not find constant multiplicand or multiplier', <_EquationMember>[this, m] ); } 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((Term t) => buffer.write('$t')); if (constant != 0.0) { buffer.write(constant.sign > 0.0 ? '+' : '-'); buffer.write(constant.abs()); } return buffer.toString(); } }