comparison nono.py @ 7:13f600be3a1b

implemented newBlob and found0, refactoring display
author Henry S. Thompson <ht@inf.ed.ac.uk>
date Wed, 11 Mar 2020 17:13:05 +0000
parents a56d5285575b
children 3276cf6ee6df
comparison
equal deleted inserted replaced
6:a56d5285575b 7:13f600be3a1b
26 list.__init__(self,list(range(n))) 26 list.__init__(self,list(range(n)))
27 self.n=n 27 self.n=n
28 self.margin0=0 # a run can start here, so if >0 then self[m0-1].val must be False 28 self.margin0=0 # a run can start here, so if >0 then self[m0-1].val must be False
29 self.marginN=n-1 # a run can end here, so if <n-1 then self[mN+1].val ditto 29 self.marginN=n-1 # a run can end here, so if <n-1 then self[mN+1].val ditto
30 self.runs=self.origRuns=runs 30 self.runs=self.origRuns=runs
31 self.initialComplete=[] 31 self.initialComplete=""
32 self.finalComplete=[] 32 self.finalComplete=""
33 self.resetAllRuns() 33 self.resetAllRuns()
34 34
35 def __repr__(self): 35 def __repr__(self):
36 return "V@%s%s:%s"%(self.x,self.origRuns,list.__repr__(self)) 36 return "V@%s%s:%s"%(self.x,self.origRuns,list.__repr__(self))
37 37
38 def __str__(self): 38 def __str__(self):
39 return '%s|'%('|'.join(str(c) for c in self)) 39 return '%s|'%('|'.join(str(c) for c in self))
40
41 def myPrintSize(self):
42 return sum(len(str(run)) for run in self.runs)+len(self.runs)-1
40 43
41 def resetAllRuns(self): 44 def resetAllRuns(self):
42 # compute the set of all possible layouts for runs 45 # compute the set of all possible layouts for runs
43 self.rn=len(self.runs) 46 self.rn=len(self.runs)
44 rtot=sum(self.runs) 47 rtot=sum(self.runs)
69 scratch=[0 if c.val is None else c.val for c in self] 72 scratch=[0 if c.val is None else c.val for c in self]
70 for k,runs in enumerate(self.allRuns): 73 for k,runs in enumerate(self.allRuns):
71 dprint('=====pass %s======'%k) 74 dprint('=====pass %s======'%k)
72 self.onepass(0,self.n,scratch,runs.copy()) 75 self.onepass(0,self.n,scratch,runs.copy())
73 dprint(scratch) 76 dprint(scratch)
77 newSeq=False
78 inSeq=False
74 for i in range(self.n): 79 for i in range(self.n):
75 if scratch[i]==self.nar: 80 if scratch[i]==self.nar:
76 # If blobby in _every_ pass, then must be a blob 81 # If blobby in _every_ pass, then must be a blob
82 if inSeq:
83 newSeq=False
84 else:
85 inSeq=True
86 newSeq=True
77 if self[i].val is None: 87 if self[i].val is None:
78 self[i].setVal(True) 88 self.newBlob(i,newSeq)
79 elif self[i].val is True: 89 elif self[i].val is True:
80 # already there 90 # already there
81 pass 91 pass
82 else: 92 else:
83 print("Shouldn't happen: attempt to blob where x already present! %s at %s"%(self,i),file=sys.stderr) 93 print("Shouldn't happen: attempt to blob where x already present! %s at %s"%(self,i),file=sys.stderr)
84 exit(101) 94 exit(101)
95 else:
96 inSeq=newSeq=False
85 97
86 def onepass(self,i0,iBound,scratch,stack): 98 def onepass(self,i0,iBound,scratch,stack):
87 """note that stack is not a simple run, but one with _negative_ numbers between 99 """note that stack is not a simple run, but one with _negative_ numbers between
88 and possibly before the positive ones, indicating obligatory skips 100 and possibly before the positive ones, indicating obligatory skips
89 """ 101 """
197 # Assuming @@ FTTB that it's never possible to definitively complete a 209 # Assuming @@ FTTB that it's never possible to definitively complete a
198 # non-marginal run, which is wrong if the length is unique and it's bounded... 210 # non-marginal run, which is wrong if the length is unique and it's bounded...
199 changed=False 211 changed=False
200 # First, find our boundaries: 212 # First, find our boundaries:
201 s=pos # start index of our run 213 s=pos # start index of our run
202 while s>self.marginO and self[s-1].val is True: 214 while s>self.margin0 and self[s-1].val is True:
203 s-=1 215 s-=1
204 f=pos # finish index of our run 216 f=pos # finish index of our run
205 while f<self.marginN and self[f+1].val is True: 217 while f<self.marginN and self[f+1].val is True:
206 f+=1 218 f+=1
207 l=(f-s)+1 # our length 219 l=(f-s)+1 # our length
211 dprint('n0') 223 dprint('n0')
212 return 224 return
213 j=self.runs.index(l) 225 j=self.runs.index(l)
214 if j==0: 226 if j==0:
215 # is it safely left marginal, i.e. no blobs or big enough gaps before us? 227 # is it safely left marginal, i.e. no blobs or big enough gaps before us?
216 if marginal(range(self.margin0,s)): 228 if self.marginal(range(self.margin0,s),l):
217 changed=True 229 changed=True
218 # mark our margins 230 # mark our margins
219 for i in range(margin0+1,s): 231 for i in range(self.margin0+1,s):
220 if self[i].val is None: 232 if self[i].val is None:
221 self[i].setVal(False) 233 self[i].setVal(False)
222 if f<marginN-1: 234 if f<self.marginN-1:
223 if self[f+1].val is None: 235 if self[f+1].val is None:
224 self[f+1].setVal(False) 236 self[f+1].setVal(False)
225 if f<marginN-2 and self[f+2].val is True: 237 if f<self.marginN-2 and self[f+2].val is True:
226 dprint('n1') 238 dprint('n1')
227 self.checkNew(f+2) # I think@@ 239 self.checkNew(f+2) # I think@@
228 self.found0(f) # pull in the start margin at least to f+2 240 self.found0(f) # pull in the start margin at least to f+2
229 elif j==self.rn-1: 241 elif j==self.rn-1:
230 # is it safely _right_ marginal, i.e. no blobs or big enough gaps _after_ us? 242 # is it safely _right_ marginal, i.e. no blobs or big enough gaps _after_ us?
231 if self.marginal(range(self.marginN-1,r,-1),l): 243 if self.marginal(range(self.marginN-1,r,-1),l):
232 changed=True 244 changed=True
233 # mark our margins 245 # mark our margins
234 for i in range(marginN-1,f,-1): 246 for i in range(self.marginN-1,f,-1):
235 if self[i].val is None: 247 if self[i].val is None:
236 self[i].setVal(False) 248 self[i].setVal(False)
237 if s>margin0+1: 249 if s>self.margin0+1:
238 if self[s-1].val is None: 250 if self[s-1].val is None:
239 self[s-1].setVal(False) 251 self[s-1].setVal(False)
240 if s>margin0+2 and self[s-2].val is True: 252 if s>self.margin0+2 and self[s-2].val is True:
241 dprint('n2') 253 dprint('n2')
242 self.checkNew(s-2) # I think@@ 254 self.checkNew(s-2) # I think@@
243 self.foundN(s) # pull in the finish margin at least to s-2 255 self.foundN(s) # pull in the finish margin at least to s-2
244 if changed: 256 if changed:
245 self.resetAllRuns() 257 self.resetAllRuns()
256 g+=1 268 g+=1
257 if g==l: 269 if g==l:
258 return False 270 return False
259 return True 271 return True
260 272
273 def found0(self,i):
274 dprint('found0 called on %s at %s'%(self,i))
275 i=self.margin0
276 while self[i].val is False:
277 i+=1
278 if self[i].val is True:
279 r=self.runs.pop(0)
280 self.initialComplete+=(RedFmt%r)
281 self.margin0+=r+1
282
283 def foundN(self,i):
284 print('foundN called on %s at %s'%(self,i),file=sys.stderr)
285
261 class Row(Vector): 286 class Row(Vector):
262 def __init__(self,n,m,runs,pos,dprintWidth): 287 def __init__(self,n,m,runs,pos):
263 Vector.__init__(self,n,m,runs) 288 Vector.__init__(self,n,m,runs)
264 self.y=pos 289 self.y=pos
265 self.dprintWidth=dprintWidth 290 self.width=self.myPrintSize()
266 self.fmt="%%%ss|"%dprintWidth
267 291
268 def __str__(self): 292 def __str__(self):
269 return ((self.fmt%(' '.join(str(r) for r in self.runs)))+ 293 return ((self.fmt%(' '.join(str(r) for r in self.runs)))+
270 Vector.__str__(self)) 294 Vector.__str__(self))
271 295
296 def updateHeader(self,maxWidth):
297 self.maxWidth=maxWidth
298 self.fmt="%s%%s|"%(' '*(maxWidth-self.width))
299
300 def newBlob(self,x,newSeq):
301 self[x].setVal(True)
302 self[x].column.checkNew(self.y)
303 if newSeq:
304 # We don't always check ourself, to avoid unnecessary duplication...
305 self.checkNew(x)
306
272 class Column(Vector): 307 class Column(Vector):
273 def __init__(self,n,m,runs,pos,dprintHeight): 308 def __init__(self,n,m,runs,pos):
274 Vector.__init__(self,n,m,runs) 309 Vector.__init__(self,n,m,runs)
275 self.x=pos 310 self.x=pos
276 self.dprintHeight=dprintHeight 311 self.height=self.myPrintSize()
277 self.fmt="%%%ss"%self.dprintHeight 312
278 self.updateHeader() 313 def updateHeader(self,maxHeight):
279 314 self.maxHeight=maxHeight
280 def updateHeader(self): 315 self.fmt="%s%%s"%(' '*(maxHeight - self.height))
281 header=('-'.join(str(c) for c in self.runs)) 316 header=('-'.join(str(c) for c in self.runs))
282 self.header=self.fmt%header # pad to same 'height' 317 self.header=self.fmt%header # pad to same 'height'
318
319 def newBlob(self,y,newSeq):
320 self[y].setVal(True)
321 self[y].row.checkNew(self.x)
322 if newSeq:
323 # We don't always check ourself, to avoid unnecessary duplication...
324 self.checkNew(y)
283 325
284 class Cell: 326 class Cell:
285 def __init__(self,row,y,column,x): 327 def __init__(self,row,y,column,x):
286 # At the intersection of row and column Vectors 328 # At the intersection of row and column Vectors
287 self.row=row 329 self.row=row
304 print("Warning: x -> * at %s,%s"%(self.x,self.y),file=sys.stderr) 346 print("Warning: x -> * at %s,%s"%(self.x,self.y),file=sys.stderr)
305 elif self.val is True: 347 elif self.val is True:
306 # No-op 348 # No-op
307 return 349 return
308 self.val=v 350 self.val=v
309 self.row.checkNew(self.x)
310 self.column.checkNew(self.y)
311 # @@ check row/col completed
312 else: 351 else:
313 if self.val is not None: 352 if self.val is not None:
314 print("Warning: %s -> %s at %s,%s"%(self.val,v,self.x,self.y),file=sys.stderr) 353 print("Warning: %s -> %s at %s,%s"%(self.val,v,self.x,self.y),file=sys.stderr)
315 self.val=v 354 self.val=v
316 355
317 class Nono(dict): 356 class Nono(dict):
318 # 0,0 is upper left, so increasing y goes _downwards_, to match the standard layout 357 # 0,0 is upper left, so increasing y goes _downwards_, to match the standard layout
319 def __init__(self,rows,cols): 358 def __init__(self,runsPerRow,runsPerCol):
320 n=self.n=len(cols) 359 n=self.n=len(runsPerCol)
321 if n!=len(rows): 360 if n!=len(runsPerRow):
322 print("losing r:%s x c:%s"%(len(rows),n),sys.stderr) 361 print("losing r:%s x c:%s"%(len(runsPerRow),n),sys.stderr)
323 exit(1) 362 exit(1)
324 self.rc=rows 363 self.rc=runsPerRow
325 rowDprintWidth=max(sum(len(str(r)) for r in row)+len(row)-1 for row in rows) 364 self.cc=runsPerCol
326 self.rowfmt="%s|%%s"%(' '*rowDprintWidth) 365 # print col nums>9 vertically :-(
327 self.cc=cols 366 self.columns=cc=[Column(n,self,runsPerCol[i],i) for i in range(20)]
328 # dprint col nums>9 vertically :-( 367 self.maxCRheight=maxCRheight=max(col.height for col in cc)
329 self.colDprintHeight=max(sum(len(str(c)) for c in col)+len(col)-1 for col in cols) 368 for c in cc:
330 self.columns=cc=[Column(n,self,cols[i],i,self.colDprintHeight) for i in range(20)] 369 c.updateHeader(maxCRheight)
331 self.rows=rr=[Row(n,self,rows[i],i,rowDprintWidth) for i in range(20)] 370 self.rows=rr=[Row(n,self,runsPerRow[i],i) for i in range(20)]
371 maxRRwidth=max(row.width for row in rr)
372 for r in rr:
373 r.updateHeader(maxRRwidth)
374 self.rowfmt="%s|%%s|"%(' '*maxRRwidth)
332 for x in range(20): 375 for x in range(20):
333 for y in range(20): 376 for y in range(20):
334 self[(x,y)]=Cell(rr[y],y,cc[x],x) 377 self[(x,y)]=Cell(rr[y],y,cc[x],x)
335 378
336 def __str__(self): 379 def __str__(self):
337 lines=[self.rowfmt%('|'.join([(self.columns[i]).header[j] for i in range(self.n)])) # 'rotate' 380 lines=[self.rowfmt%('|'.join([(self.columns[i]).header[j] for i in range(self.n)])) # 'rotate'
338 for j in range(self.colDprintHeight)] 381 for j in range(self.maxCRheight)]
339 lines+=[str(r) for r in self.rows] 382 lines+=[str(r) for r in self.rows]
340 return "\n".join(lines) 383 return "\n".join(lines)
341 384
342 def dprint(*args): 385 def dprint(*args):
343 pass 386 pass