import util.
%import cp.
%module unknown.
/* DWG field packing problem, the solver in picat-lang.org
See https://www.gnu.org/software/libredwg/ examples/unknown
At first fix all positions for unique finds: BL93,H330,BL92.
Skip conflicts, i.e. duplicate fields.
Then check for all multiple finds if the number of finds is the same as the
number of equal values: BL90,BL96,BL97. Distribute it then.
TODO Then pack the possible others optimally: smallest amount of
missing holes, and keep the original order, with backtracking.
Print the final layout of fixed and open ranges in dwg.spec syntax.
Compare to all examples of the same class.
*/
bits(S) = S[1]. % TODO filter out " " and "\n"
value(S) = S[2].
poslist(S) = S[3].
fieldname(S) = cond(S.length >= 4, S[4],
[C: C in S.name.atom_chars, ord(C) > ord('9')].to_lowercase).
dxfcode(S) = cond(S.length >= 5, S[5],
[C: C in S.name.atom_chars, ord(C) >= ord('0'), ord(C) <= ord('9')]).
bitslen(S) = S[1].length.
go(Data) =>
[S,Fields,Class,Dxf,Version,Offsets] = Data,
Found = [],
println(S), nl,
print("[hdloff, strsize, commonsize, bitsize, hdlsize]="),println(Offsets),
S := S.delete_all(" "),
S := S.delete_all("\n"),
foreach(Sub in Fields)
print(Sub.name), print(" "), print(Sub[2]), print(": "++Sub[1]),
PosList = findall([Start,End], find(S,Sub[1],Start,End)),
Sub[3] := PosList,
if PosList.length = 1 then
Found := add_found(PosList[1],Found,[Sub],Fields), % checks overlap
print(" "),println(fixed=first(Sub[3]))
else
print(" "),print(PosList.length),println(" positions"), % println(positions=PosList),
% check for matching number of multiple values
N := same_values(Fields, Sub), % all Subs with same values
if N.length = PosList.length then
print("distribute positions: "),
I = 1,
foreach(X in N, X[3].length >= I)
print(X.name),print(" "),
nth(I,X[3],T1),
Found := add_found(T1,Found,N,Fields),
% nth(I,PosList,T),
X[3] := [PosList[I]], % collapse to single entry
I := I + 1
end,
nl
end
end,
%println(fixed=Found),
end,
% At first fix all positions for one finds: BL93,H330,BL92.
% TODO Error if there's a conflict already.
% Then check for all multiple finds if the number of finds is the same as the number of
% equal values: BL90,BL96,BL97. Distribute it then.
% TODO Then pack the possible others optimally (smallest amount of missing holes),
% with backtracking.
% Print the layout of all fixed ranges and holes (yet unconstraint)
nl,println(fixed=Found),
Holes = [],
Sorted := [F: F in Fields, F[3].length == 1],
Sorted := field_sort(Sorted),
println(sortd=Sorted.map(poslist).map(first)), % just for control
% here maybe filter out all all fixed from Fields again
foreach (F in Found)
delete_found(F, [], Fields)
end,
nl,
FoundSize = sum(map(bitslen,Sorted)),
print("Definite result: "),print(FoundSize),print("/"),print(S.length),
print("\t"),println(Dxf.slice(length("test/test-data/")+1)),
println("----------------"),
% create holes by looking at all adjacent pos's
foreach (I in 1 .. Sorted.length)
Sub = Sorted[I],
Pos = first(poslist(Sub)),
if I == 1 then
if Pos[1] != 1 then
%println(pos=Pos),
H = [1, Pos[1]-1], %println(h=H),
R = S.slice(1,Pos[1]-1),
Hole = new_struct('HOLE', [H, R]),
print(Hole),print(" "),println(len=H[2]+1-H[1]),
Holes := [Hole]
end
else
Prev := Sorted[I-1],
PrevPos = first(poslist(Prev)),
%println(prevpos=PrevPos),
%println(pos=Pos),
if Pos[1] > PrevPos[2]+1 then
H = [PrevPos[2]+1, Pos[1]-1], %println(h=H),
R = S.slice(PrevPos[2]+1, Pos[1]-1), %println(r=R),
Hole = new_struct('HOLE', [H, R]),
print(Hole),print(" "),println(len=H[2]+1-H[1]),
append(Holes, [Hole], T), Holes := T
end
end,
print_field(Sub,false)
end,
% maybe one last hole
if Sorted.length > 1 then
Sub = last(Sorted), Pos = first(poslist(Sub)),
if Pos[2] < S.length then
H = [Pos[2]+1, S.length], %println(h=H),
R = S.slice(Pos[2]+1, S.length), %println(r=R),
Hole = new_struct('HOLE', [H, R]),
print(Hole),print(" "),println(len=H[2]+1-H[1]),
append(Holes, [Hole], T), Holes := T
end
end,
println("----------------"),
NotFoundSize = sum(map(bitslen,[F: F in Fields, F[3].length > 1])),
BothFoundSizes = FoundSize+NotFoundSize,
print("Todo: "),print(NotFoundSize),print(" + "),print(FoundSize),
print(" = "),print(BothFoundSizes),
print(", Missing: "),println(S.length-BothFoundSizes),
foreach (F in Fields, F[3].length > 1)
print_field(F,true)
end,
nl,
println("Most probable result: (TODO)"),
% TODO: fill holes by probable types (cp or sat?)
% e.g. if the Hole length is 66 it is most likely a BD, 64 RD, ...
% check for the nan range then.
nl.
% H330(01100000,2E4,[[192,199]],reactors,330) =>
% FIELD_H (reactors, 330); // 2E4 01100000 191-198
print_field(Sub,ShowBits) =>
% split name into type and dxf
Name = name(Sub), L = poslist(Sub),
Type = [C: C in Name.atom_chars, ord(C) > ord('9')],
DXF = Sub.dxfcode,
print("FIELD_"),print(Type),print(" ("),
print(Sub.fieldname),
print(", "),print(DXF),print(");\t// "),
print(Sub.value),print(" "),
if ShowBits then print(Sub.bits),print(" ") end,
if L.length > 10 then
print(L.slice(1,10)),
print("...)")
else
if L.length == 1 then
print(L[1])
else
print(L)
end
end,
nl.
% list of same values and number of solutions of Sub in Fields
same_values(Fields, Sub) =
% && or , for /\ here: "compound condition". ; would be or
[X : X in Fields, X[3].length == Sub[3].length && X[1] = Sub[1]].
% delete found pos from all other fields. changes Fields destructively
delete_found(Pos, Subs, Fields) =>
%print("delete_found "),print(pos=Pos),print(" "),println(sub=Sub),
foreach(S in Fields, S[3].length > 1, not member(S, Subs))
P = S[3], %print(P),
Before = [P1: P1 in P, P1[1] < Pos[1]],
After = [P2: P2 in P, P2[1] > Pos[2]],
append(Before, After, T),
%if T.length != P.length then print("=>"), println(T) end,
S[3] := T
end.
% add found pos if not already in.
% check valid ranges, change Found inline, keep Found sorted.
add_found(Pos, [], Subs, Fields) = List =>
List = [Pos],
delete_found(Pos, Subs, Fields).
% backtracking not needed yet
add_found(Pos, Found, Subs, Fields) = List =>
%print("add_found "),print(pos=Pos),print(" "),println(found=Found),
% sort Pos into Found: pos = [33,42] found = [[192,199]]
Before = [X1: X1 in Found, Pos[1] > X1[1], Pos[2] > X1[1]],
append(Before, [Pos], T1),
% and the remaining elems behind. pos = [43,52] found = [[33,42],[192,199]]
Behind = [X2: X2 in Found, Pos[1] < X2[1], Pos[2] < X2[1]],
append(T1, Behind, T2),
if Found.length != T2.length-1 then
print(" Ignored overlap "), List = Found
else
List = T2,
delete_found(Pos, Subs, Fields)
end.
% sort Field struct by first pos (simple bubble sort)
field_sort(T) = Ret =>
T2 = copy_term(T),
Len = T.length,
Check = true,
while(Check = true)
Swapped = false,
foreach(I in 2..Len)
P = T2[I-1], C = T2[I],
if first(first(P[3])) > first(first(C[3])) then
T2 := swap(T2,I-1,I),
Swapped := true % interchange the items.
end
end,
if Swapped = false then
Check := false
end
end,
Ret = T2.
swap(L,I,J) = L2, list(L) =>
L2 = copy_term(L),
T = L2[I],
L2[I] := L2[J],
L2[J] := T.
%nth(I,List) = Elem =>
% K = 0,
% while (K<I) do
% L := List.head
% end,
% Elem = L.