summaryrefslogtreecommitdiff
path: root/libs/cassowary/ClFDBinaryOneWayConstraint.cc
blob: e7bf7f10892ed16ce9ab29f811acb66563a51765 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
// $Id$
//
// Cassowary Incremental Constraint Solver
// Original Smalltalk Implementation by Alan Borning
// This C++ Implementation by Greg J. Badros, <gjb@cs.washington.edu>
// http://www.cs.washington.edu/homes/gjb
// (C) 1998, 1999 Greg J. Badros and Alan Borning
// See ../LICENSE for legal details regarding this software
//
// ClFDBinaryOneWayConstraint.cc


#ifdef HAVE_CONFIG_H
#include <config.h>
#define CONFIG_H_INCLUDED
#endif

#include <cassowary/ClFDBinaryOneWayConstraint.h>
#include <cassowary/ClLinearConstraint.h>
#include <cassowary/ClTypedefs.h>
#include <cassowary/ClLinearExpression.h>


void 
ClFDBinaryOneWayConstraint::EnsurePreconditionsForCn(const ClConstraint &cn)
{
  ClVarSet setRO = cn.ReadOnlyVars();
  if (setRO.size() > 1) 
    throw ExCLTooDifficultSpecial("Only 0 or 1 read only variables are allowed");
  const ClLinearExpression &expr = cn.Expression();
  const ClVarToNumberMap &terms = expr.Terms();
  if (terms.size() > 2)
    throw ExCLTooDifficultSpecial("Cannot have more than 2 variables");
  if (terms.size() == 0)
    throw ExCLTooDifficultSpecial("Must have at least 1 variable");
  if (terms.size() == 2 && setRO.size() == 0)
    throw ExCLTooDifficultSpecial("Both variables cannot be read-write, one must be read-only");
  if (terms.size() == 1 && setRO.size() == 1)
    throw ExCLTooDifficultSpecial("Single read-only variable in LinearConstraint -- must not be read-only.");
  ClVariable clv = (*terms.begin()).first;
  /* GJB:FIXME:: iterate over all the variables */
  if (!clv->IsFDVariable()) {
    throw ExCLTooDifficultSpecial("FD constraint contains non-FD variables");
  }
}

bool 
ClFDBinaryOneWayConstraint::FCanConvertCn(const ClConstraint &cn)
{
  try {
    EnsurePreconditionsForCn(cn);
    return true;
  } catch (...) {
    return false;
  }
}


ClFDBinaryOneWayConstraint::ClFDBinaryOneWayConstraint(const ClConstraint &cn)
    :ClFDConstraint(cn.strength(), cn.weight())
{
  EnsurePreconditionsForCn(cn);
  list<FDNumber> l;
  /* GJB:FIXME:: varargs inteface, with sentinel as first arg? */
  l.push_back(9);
  l.push_back(10);
  l.push_back(12);
  l.push_back(14);
  l.push_back(20);

  ClVarSet setRO = cn.ReadOnlyVars();

  ClVariable clvRO = clvNil;
  ClVariable clvROLinear = clvNil;
  Number coeffRO = 0;

  ClVariable clvRW = clvNil;
  Number coeffRW = 0;

  if (setRO.size() == 1) {
    const ClVariable &clv = *(setRO.begin());
    if (clv->IsFDVariable())
      clvRO = clv;
    else
      clvRO = new ClFDVariable(clv.Name(),clv.IntValue(),l);
    clvROLinear = clv;
  }
  const ClLinearExpression &expr = cn.Expression();
  const ClVarToNumberMap &terms = expr.Terms();

  for (ClVarToNumberMap::const_iterator it = terms.begin();
       it != terms.end();
       ++it) {
    ClVariable clv = (*it).first;
    if (clv == clvROLinear) {
      coeffRO = (*it).second;
    } else {
      if (clv->IsFDVariable())
        clvRW = clv;
      else
        clvRW = new ClFDVariable(clv.Name(),clv.Value(),l);
      coeffRW = (*it).second;
    }
  }
  assert(!clvRW.IsNil());
  if (coeffRW == 0) {
    throw ExCLTooDifficultSpecial("RW variable's coefficient must be non-zero");
  }

  bool fInequality = cn.IsInequality();
  bool fStrictInequality = cn.IsStrictInequality();
  double rhs_constant = expr.Constant();

  // now we have:
  // coeffRW * clvRW + coeffRO * clvRO <REL> rhs_constant
  //   where <REL> is >= if fInequality, or = if !fInequality
  // 
  // need:
  //   clvRW <REL> coefficient * clvRO + constant
  // 
  // so:
  // coefficient = -coeffRO/coeffRW
  // constant = rhs_constant/coeffRW

  if (fStrictInequality)
    _rel = cnGT;
  else if (fInequality)
    _rel = cnGEQ;
  else
    _rel = cnEQ;

  if (coeffRW < 0)
    _rel = ReverseInequality(_rel);

  _coefficient = -coeffRO/coeffRW;
  _constant = -rhs_constant/coeffRW;
  _vRW = clvRW;
  _vRO = clvRO;
  return;
}