package facile

  1. Overview
  2. Docs

Source file fcl_alldiff.ml

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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
(***********************************************************************)
(*                                                                     *)
(*                           FaCiLe                                    *)
(*                 A Functional Constraint Library                     *)
(*                                                                     *)
(*            Nicolas Barnier, Pascal Brisset, LOG, CENA               *)
(*                                                                     *)
(* Copyright 2004 CENA. All rights reserved. This file is distributed  *)
(* under the terms of the GNU Lesser General Public License.           *)
(***********************************************************************)
(* $Id: fcl_alldiff.ml,v 1.22 2004/08/12 15:22:07 barnier Exp $ *)

module C = Fcl_cstr

open Fcl_var


let such_that f t =
  let n = Array.length t in
  let rec st acc i =
    if i >= 0
    then st (if f t.(i) then (i::acc) else acc) (i-1)
    else acc in
  st [] (n-1);;


let new_diff_from_others (vars : Fd.t array) =
  let name = "diff_from_others"
  and delay x =
    Array.iteri
      (fun i _vi -> delay [Fd.on_subst] vars.(i) ~waking_id:i x)
      vars

  and update i =
    match Fd.value vars.(i) with
      Unk _ -> false
    | Val x ->
	for j = 0 to Array.length vars - 1 do
	  if i <> j
	  then match Fd.value vars.(j) with
	    Unk a -> Fd.refine vars.(j) (Fcl_domain.remove x (Attr.dom a))
	  | Val y -> if x = y then Fcl_stak.fail "diff_from_others"
	done;
  	true in

  C.create ~name ~nb_wakings:(Array.length vars) ~priority:C.immediate
    update delay



module Int = struct
  type t = int
  let equal (x : int) y = x = y
  let hash (x : int) = Hashtbl.hash x
end

module IntHashtbl = Hashtbl.Make(Int)
    

let new_diff (vars : Fd.t array) on_event = 
  let n = Array.length vars
      (* Une valeur en dehors des valeurs possibles *)
  and valout = Array.fold_left (fun acc v -> min acc (Fd.min v)) max_int vars - 1 in
  let matchingl = Array.make n valout in (* var -> value : not backtrackable *)
  let matchingr = IntHashtbl.create n in(* value -> var : not backtrackable *)

  let name = "Permut.diff"
  and delay x = Array.iter (fun v -> delay [on_event] v x) vars

  and update _ =
    let getmatchingr i = try IntHashtbl.find matchingr i with Not_found -> -1 in
      (* matching update (values have been removed from the domains) *)
    let lefts = ref [] in
    for i = n-1 downto 0 do (* downto to get lefts ordered *)
      if matchingl.(i) = valout
	then lefts := i :: !lefts
      else match Fd.value vars.(i) with
	Unk a ->
	  if not (Fcl_domain.member matchingl.(i) (Attr.dom a)) then begin
	    IntHashtbl.remove matchingr matchingl.(i);
	    matchingl.(i) <- valout;
	      lefts := i :: !lefts
	  end
      | Val x ->
	  if x <> matchingl.(i)
	  then begin
	    let y = getmatchingr x in
	    if y <> -1 (* Value x was already used by y *)
	    then begin
	      matchingl.(y) <- valout;
	      IntHashtbl.remove matchingr x;
	      lefts := y :: !lefts
	    end;
	    if matchingl.(i) <> valout then (*=valout the first time*)
	      IntHashtbl.remove matchingr matchingl.(i);
	    matchingl.(i) <- x;
	    assert(not (IntHashtbl.mem matchingr x));
	    IntHashtbl.add matchingr x i
	  end
    done;
    if !lefts <> [] then begin
      let apath = IntHashtbl.create n in
      let getapath i = try IntHashtbl.find apath i with Not_found -> -1 in
      let reset_apath () = IntHashtbl.clear apath in
      
      let depth_first rights =
	let rec reverse right left =
	  let r = matchingl.(left) in
	  matchingl.(left) <- right;
	  if r <> valout
	  then begin
	    let a = IntHashtbl.find apath r in
	    assert(IntHashtbl.mem matchingr r);
	    IntHashtbl.remove matchingr r;
	    IntHashtbl.add matchingr r a;
	    IntHashtbl.remove apath r;
	    reverse r a
	  end
	and check r =
	  let ar = getapath r in
	  ar <> -1 &&
	  (matchingl.(ar) = valout || check (matchingl.(ar))) in
	List.iter
	  (fun r ->
	    if getmatchingr r = -1 && check r then begin
	      let a = IntHashtbl.find apath r in
	      assert(not (IntHashtbl.mem matchingr r));
	      IntHashtbl.add matchingr r a;
	      IntHashtbl.remove apath r;
	      reverse r a
	    end)
	  rights
      in
      
      let rec breadth_first lefts =
	lefts <> [] && begin
	  let ending = ref false
	  and rights = ref []
	  and new_lefts = ref [] in
	  List.iter
	    (fun l ->
	      try
        	Fd.iter
		  (fun r ->
		    if getapath r = -1 then begin
		      assert(not (IntHashtbl.mem apath r));
		      IntHashtbl.add apath r l;
		      rights := r :: !rights;
		      let mr = getmatchingr r in
		      if mr = -1
		      then raise Exit
		      else new_lefts := mr :: !new_lefts
		    end) vars.(l)
	      with
		Exit -> ending := true)
	    lefts;
	  if !ending
	  then begin
	    depth_first !rights;
	    let lefts = such_that (fun xi -> xi = valout) matchingl in
	    lefts = [] || (reset_apath (); breadth_first lefts)
	  end else breadth_first !new_lefts
	end in
      if not (breadth_first !lefts) then Fcl_stak.fail "permut"
    end;
    false
  in
  C.create ~priority:C.later ~name update delay

type algo = Lazy | Bin_matching of Attr.event

let cstr ?(algo = Lazy) vars =
  (* vars is copied to prevent modifications by the user *)
  let vars = Array.copy vars in
  let n = Array.length vars in
  if n <= 1 then Fcl_cstr.one else
  let dfo = new_diff_from_others vars in
  match algo with
    Bin_matching on_event ->
      Fcl_cstr.conjunction [new_diff vars on_event; dfo]
  | Lazy -> dfo