expression.dart 4.82 KB
Newer Older
Chinmay Garde's avatar
Chinmay Garde committed
1 2 3 4 5 6
// 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;

7
class Expression extends _EquationMember {
Chinmay Garde's avatar
Chinmay Garde committed
8
  final List<Term> terms;
9

Chinmay Garde's avatar
Chinmay Garde committed
10
  final double constant;
11 12 13

  bool get isConstant => terms.length == 0;

Chinmay Garde's avatar
Chinmay Garde committed
14 15 16
  double get value => terms.fold(constant, (value, term) => value + term.value);

  Expression(this.terms, this.constant);
17 18 19
  Expression.fromExpression(Expression expr)
      : this.terms = new List<Term>.from(expr.terms),
        this.constant = expr.constant;
Chinmay Garde's avatar
Chinmay Garde committed
20

21 22
  Expression asExpression() => this;

23
  Constraint _createConstraint(
24
      _EquationMember /* rhs */ value, Relation relation) {
25
    if (value is ConstantMember) {
26 27 28
      return new Constraint(
          new Expression(new List.from(terms), constant - value.value),
          relation);
29 30
    }

31 32 33
    if (value is Param) {
      var newTerms = new List<Term>.from(terms)
        ..add(new Term(value.variable, -1.0));
34
      return new Constraint(new Expression(newTerms, constant), relation);
35 36 37
    }

    if (value is Term) {
38
      var newTerms = new List<Term>.from(terms)
39
        ..add(new Term(value.variable, -value.coefficient));
40
      return new Constraint(new Expression(newTerms, constant), relation);
41 42 43
    }

    if (value is Expression) {
44
      var newTerms = value.terms.fold(new List<Term>.from(terms),
45 46
          (list, t) => list..add(new Term(t.variable, -t.coefficient)));
      return new Constraint(
47
          new Expression(newTerms, constant - value.constant), relation);
48 49 50 51
    }

    assert(false);
    return null;
Chinmay Garde's avatar
Chinmay Garde committed
52 53
  }

54
  Constraint operator >=(_EquationMember value) =>
55
      _createConstraint(value, Relation.greaterThanOrEqualTo);
Chinmay Garde's avatar
Chinmay Garde committed
56

57
  Constraint operator <=(_EquationMember value) =>
58
      _createConstraint(value, Relation.lessThanOrEqualTo);
Chinmay Garde's avatar
Chinmay Garde committed
59

60
  operator ==(_EquationMember value) =>
Hixie's avatar
Hixie committed
61
    _createConstraint(value, Relation.equalTo); // analyzer says "Type check failed" // analyzer says "The return type 'Constraint' is not a 'bool', as defined by the method '=='"
Chinmay Garde's avatar
Chinmay Garde committed
62

63
  Expression operator +(_EquationMember m) {
Chinmay Garde's avatar
Chinmay Garde committed
64
    if (m is ConstantMember) {
65
      return new Expression(new List.from(terms), constant + m.value);
Chinmay Garde's avatar
Chinmay Garde committed
66 67
    }

68
    if (m is Param) {
Chinmay Garde's avatar
Chinmay Garde committed
69
      return new Expression(
70
          new List.from(terms)..add(new Term(m.variable, 1.0)), constant);
Chinmay Garde's avatar
Chinmay Garde committed
71 72 73
    }

    if (m is Term) {
74
      return new Expression(new List.from(terms)..add(m), constant);
Chinmay Garde's avatar
Chinmay Garde committed
75 76 77
    }

    if (m is Expression) {
78 79
      return new Expression(
          new List.from(terms)..addAll(m.terms), constant + m.constant);
Chinmay Garde's avatar
Chinmay Garde committed
80 81 82 83 84 85
    }

    assert(false);
    return null;
  }

86
  Expression operator -(_EquationMember m) {
Chinmay Garde's avatar
Chinmay Garde committed
87
    if (m is ConstantMember) {
88
      return new Expression(new List.from(terms), constant - m.value);
Chinmay Garde's avatar
Chinmay Garde committed
89 90
    }

91
    if (m is Param) {
Chinmay Garde's avatar
Chinmay Garde committed
92
      return new Expression(
93
          new List.from(terms)..add(new Term(m.variable, -1.0)), constant);
Chinmay Garde's avatar
Chinmay Garde committed
94 95 96
    }

    if (m is Term) {
97 98
      return new Expression(new List.from(terms)
        ..add(new Term(m.variable, -m.coefficient)), constant);
Chinmay Garde's avatar
Chinmay Garde committed
99 100 101
    }

    if (m is Expression) {
102
      var copiedTerms = new List<Term>.from(terms);
Chinmay Garde's avatar
Chinmay Garde committed
103 104
      m.terms.forEach(
          (t) => copiedTerms.add(new Term(t.variable, -t.coefficient)));
105
      return new Expression(copiedTerms, constant - m.constant);
Chinmay Garde's avatar
Chinmay Garde committed
106 107 108 109 110 111
    }

    assert(false);
    return null;
  }

112
  _EquationMember _applyMultiplicand(double m) {
113
    var newTerms = terms.fold(new List<Term>(), (list, term) => list
Chinmay Garde's avatar
Chinmay Garde committed
114
      ..add(new Term(term.variable, term.coefficient * m)));
115
    return new Expression(newTerms, constant * m);
Chinmay Garde's avatar
Chinmay Garde committed
116 117
  }

118
  _Pair<Expression, double> _findMulitplierAndMultiplicand(_EquationMember m) {
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
    // 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;
  }

138
  _EquationMember operator *(_EquationMember m) {
139 140 141 142
    _Pair<Expression, double> args = _findMulitplierAndMultiplicand(m);

    if (args == null) {
      throw new ParserException(
Hixie's avatar
Hixie committed
143
          'Could not find constant multiplicand or multiplier', [this, m]);
144 145 146 147 148 149
      return null;
    }

    return args.first._applyMultiplicand(args.second);
  }

150
  _EquationMember operator /(_EquationMember m) {
151 152
    if (!m.isConstant) {
      throw new ParserException(
Hixie's avatar
Hixie committed
153
          'The divisor was not a constant expression', [this, m]);
154 155 156 157
      return null;
    }

    return this._applyMultiplicand(1.0 / m.value);
Chinmay Garde's avatar
Chinmay Garde committed
158
  }
159 160 161 162

  String toString() {
    StringBuffer buffer = new StringBuffer();

Hixie's avatar
Hixie committed
163
    terms.forEach((t) => buffer.write('$t'));
164 165

    if (constant != 0.0) {
Hixie's avatar
Hixie committed
166
      buffer.write(constant.sign > 0.0 ? '+' : '-');
167 168 169 170 171
      buffer.write(constant.abs());
    }

    return buffer.toString();
  }
Chinmay Garde's avatar
Chinmay Garde committed
172
}