Sat, 03 Jan 2015 20:18:00 +0100
Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.
michael@0 | 1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
michael@0 | 2 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 5 | |
michael@0 | 6 | |
michael@0 | 7 | //----------------------------------------------------------------------------- |
michael@0 | 8 | var BUGNUMBER = 380237; |
michael@0 | 9 | var summary = 'Generator expressions - sudoku'; |
michael@0 | 10 | var actual = ''; |
michael@0 | 11 | var expect = ''; |
michael@0 | 12 | |
michael@0 | 13 | |
michael@0 | 14 | //----------------------------------------------------------------------------- |
michael@0 | 15 | test(); |
michael@0 | 16 | //----------------------------------------------------------------------------- |
michael@0 | 17 | |
michael@0 | 18 | function test() |
michael@0 | 19 | { |
michael@0 | 20 | enterFunc ('test'); |
michael@0 | 21 | printBugNumber(BUGNUMBER); |
michael@0 | 22 | printStatus (summary); |
michael@0 | 23 | |
michael@0 | 24 | if (this.version) version(180); |
michael@0 | 25 | |
michael@0 | 26 | // XXX should be standard (and named clone, after Java?) |
michael@0 | 27 | Object.prototype.copy = function () { |
michael@0 | 28 | let o = {} |
michael@0 | 29 | for (let i in this) |
michael@0 | 30 | o[i] = this[i] |
michael@0 | 31 | return o |
michael@0 | 32 | } |
michael@0 | 33 | |
michael@0 | 34 | // Make arrays and strings act more like Python lists by iterating their values, not their keys. |
michael@0 | 35 | Array.prototype.__iterator__ = String.prototype.__iterator__ = function () { |
michael@0 | 36 | for (let i = 0; i < this.length; i++) |
michael@0 | 37 | yield this[i] |
michael@0 | 38 | } |
michael@0 | 39 | |
michael@0 | 40 | // Containment testing for arrays and strings that should be coherent with their __iterator__. |
michael@0 | 41 | Array.prototype.contains = String.prototype.contains = function (e) { |
michael@0 | 42 | return this.indexOf(e) != -1 |
michael@0 | 43 | } |
michael@0 | 44 | |
michael@0 | 45 | Array.prototype.repeat = String.prototype.repeat = function (n) { |
michael@0 | 46 | let s = this.constructor() |
michael@0 | 47 | for (let i = 0; i < n; i++) |
michael@0 | 48 | s = s.concat(this) |
michael@0 | 49 | return s |
michael@0 | 50 | } |
michael@0 | 51 | |
michael@0 | 52 | String.prototype.center = function (w) { |
michael@0 | 53 | let n = this.length |
michael@0 | 54 | if (w <= n) |
michael@0 | 55 | return this |
michael@0 | 56 | let m = Math.floor((w - n) / 2) |
michael@0 | 57 | return ' '.repeat(m) + this + ' '.repeat(w - n - m) |
michael@0 | 58 | } |
michael@0 | 59 | |
michael@0 | 60 | Array.prototype.toString = Array.prototype.toSource |
michael@0 | 61 | Object.prototype.toString = Object.prototype.toSource |
michael@0 | 62 | |
michael@0 | 63 | // XXX thought spurred by the next two functions: array extras should default to identity function |
michael@0 | 64 | |
michael@0 | 65 | function all(seq) { |
michael@0 | 66 | for (let e in seq) |
michael@0 | 67 | if (!e) |
michael@0 | 68 | return false |
michael@0 | 69 | return true |
michael@0 | 70 | } |
michael@0 | 71 | |
michael@0 | 72 | function some(seq) { |
michael@0 | 73 | for (let e in seq) |
michael@0 | 74 | if (e) |
michael@0 | 75 | return e |
michael@0 | 76 | return false |
michael@0 | 77 | } |
michael@0 | 78 | |
michael@0 | 79 | function cross(A, B) { |
michael@0 | 80 | return [a+b for (a in A) for (b in B)] |
michael@0 | 81 | } |
michael@0 | 82 | |
michael@0 | 83 | function dict(A) { |
michael@0 | 84 | let d = {} |
michael@0 | 85 | for (let e in A) |
michael@0 | 86 | d[e[0]] = e[1] |
michael@0 | 87 | return d |
michael@0 | 88 | } |
michael@0 | 89 | |
michael@0 | 90 | function set(A) { |
michael@0 | 91 | let s = [] |
michael@0 | 92 | for (let e in A) |
michael@0 | 93 | if (!s.contains(e)) |
michael@0 | 94 | s.push(e) |
michael@0 | 95 | return s |
michael@0 | 96 | } |
michael@0 | 97 | |
michael@0 | 98 | function zip(A, B) { |
michael@0 | 99 | let z = [] |
michael@0 | 100 | let n = Math.min(A.length, B.length) |
michael@0 | 101 | for (let i = 0; i < n; i++) |
michael@0 | 102 | z.push([A[i], B[i]]) |
michael@0 | 103 | return z |
michael@0 | 104 | } |
michael@0 | 105 | |
michael@0 | 106 | rows = 'ABCDEFGHI' |
michael@0 | 107 | cols = '123456789' |
michael@0 | 108 | digits = '123456789' |
michael@0 | 109 | squares = cross(rows, cols) |
michael@0 | 110 | unitlist = [cross(rows, c) for (c in cols)] |
michael@0 | 111 | .concat([cross(r, cols) for (r in rows)]) |
michael@0 | 112 | .concat([cross(rs, cs) for (rs in ['ABC','DEF','GHI']) for (cs in ['123','456','789'])]) |
michael@0 | 113 | units = dict([s, [u for (u in unitlist) if (u.contains(s))]] |
michael@0 | 114 | for (s in squares)) |
michael@0 | 115 | peers = dict([s, set([s2 for (u in units[s]) for (s2 in u) if (s2 != s)])] |
michael@0 | 116 | for (s in squares)) |
michael@0 | 117 | |
michael@0 | 118 | // Given a string of 81 digits (or . or 0 or -), return a dict of {cell:values}. |
michael@0 | 119 | function parse_grid(grid) { |
michael@0 | 120 | grid = [c for (c in grid) if ('0.-123456789'.contains(c))] |
michael@0 | 121 | let values = dict([s, digits] for (s in squares)) |
michael@0 | 122 | |
michael@0 | 123 | for (let [s, d] in zip(squares, grid)) |
michael@0 | 124 | if (digits.contains(d) && !assign(values, s, d)) |
michael@0 | 125 | return false |
michael@0 | 126 | return values |
michael@0 | 127 | } |
michael@0 | 128 | |
michael@0 | 129 | // Eliminate all the other values (except d) from values[s] and propagate. |
michael@0 | 130 | function assign(values, s, d) { |
michael@0 | 131 | if (all(eliminate(values, s, d2) for (d2 in values[s]) if (d2 != d))) |
michael@0 | 132 | return values |
michael@0 | 133 | return false |
michael@0 | 134 | } |
michael@0 | 135 | |
michael@0 | 136 | // Eliminate d from values[s]; propagate when values or places <= 2. |
michael@0 | 137 | function eliminate(values, s, d) { |
michael@0 | 138 | if (!values[s].contains(d)) |
michael@0 | 139 | return values // Already eliminated |
michael@0 | 140 | values[s] = values[s].replace(d, '') |
michael@0 | 141 | if (values[s].length == 0) |
michael@0 | 142 | return false // Contradiction: removed last value |
michael@0 | 143 | if (values[s].length == 1) { |
michael@0 | 144 | // If there is only one value (d2) left in square, remove it from peers |
michael@0 | 145 | let d2 = values[s][0] |
michael@0 | 146 | if (!all(eliminate(values, s2, d2) for (s2 in peers[s]))) |
michael@0 | 147 | return false |
michael@0 | 148 | } |
michael@0 | 149 | // Now check the places where d appears in the units of s |
michael@0 | 150 | for (let u in units[s]) { |
michael@0 | 151 | let dplaces = [s for (s in u) if (values[s].contains(d))] |
michael@0 | 152 | if (dplaces.length == 0) |
michael@0 | 153 | return false |
michael@0 | 154 | if (dplaces.length == 1) |
michael@0 | 155 | // d can only be in one place in unit; assign it there |
michael@0 | 156 | if (!assign(values, dplaces[0], d)) |
michael@0 | 157 | return false |
michael@0 | 158 | } |
michael@0 | 159 | return values |
michael@0 | 160 | } |
michael@0 | 161 | |
michael@0 | 162 | // Used for debugging. |
michael@0 | 163 | function print_board(values) { |
michael@0 | 164 | let width = 1 + Math.max.apply(Math, [values[s].length for (s in squares)]) |
michael@0 | 165 | let line = '\n' + ['-'.repeat(width*3)].repeat(3).join('+') |
michael@0 | 166 | for (let r in rows) |
michael@0 | 167 | print([values[r+c].center(width) + ('36'.contains(c) && '|' || '') |
michael@0 | 168 | for (c in cols)].join('') + ('CF'.contains(r) && line || '')) |
michael@0 | 169 | print('\n') |
michael@0 | 170 | } |
michael@0 | 171 | |
michael@0 | 172 | easy = "..3.2.6..9..3.5..1..18.64....81.29..7.......8..67.82....26.95..8..2.3..9..5.1.3.." |
michael@0 | 173 | |
michael@0 | 174 | print_board(parse_grid(easy)) |
michael@0 | 175 | |
michael@0 | 176 | // Using depth-first search and constraint propagation, try all possible values. |
michael@0 | 177 | function search(values) { |
michael@0 | 178 | if (!values) |
michael@0 | 179 | return false // Failed earlier |
michael@0 | 180 | if (all(values[s].length == 1 for (s in squares))) |
michael@0 | 181 | return values // Solved! |
michael@0 | 182 | |
michael@0 | 183 | // Choose the unfilled square s with the fewest possibilities |
michael@0 | 184 | // XXX Math.min etc. should work with generator expressions and other iterators |
michael@0 | 185 | // XXX Math.min etc. should work on arrays (lists or tuples in Python) as well as numbers |
michael@0 | 186 | let a = [values[s].length + s for (s in squares) if (values[s].length > 1)].sort() |
michael@0 | 187 | let s = a[0].slice(-2) |
michael@0 | 188 | |
michael@0 | 189 | return some(search(assign(values.copy(), s, d)) for (d in values[s])) |
michael@0 | 190 | } |
michael@0 | 191 | |
michael@0 | 192 | hard = '4.....8.5.3..........7......2.....6.....8.4......1.......6.3.7.5..2.....1.4......' |
michael@0 | 193 | |
michael@0 | 194 | print_board(search(parse_grid(hard))) |
michael@0 | 195 | |
michael@0 | 196 | delete Object.prototype.copy; |
michael@0 | 197 | |
michael@0 | 198 | reportCompare(expect, actual, summary); |
michael@0 | 199 | |
michael@0 | 200 | exitFunc ('test'); |
michael@0 | 201 | } |