trackler 2.2.1.76 → 2.2.1.77

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/lib/trackler/version.rb +1 -1
  3. data/problem-specifications/exercises/beer-song/canonical-data.json +33 -17
  4. data/problem-specifications/exercises/binary-search/canonical-data.json +41 -21
  5. data/problem-specifications/exercises/book-store/canonical-data.json +58 -30
  6. data/problem-specifications/exercises/two-fer/metadata.yml +1 -1
  7. data/tracks/bash/config.json +35 -13
  8. data/tracks/bash/exercises/acronym/README.md +27 -0
  9. data/tracks/bash/exercises/acronym/acronym.sh +18 -0
  10. data/tracks/bash/exercises/acronym/acronym_tests.sh +43 -0
  11. data/tracks/bash/exercises/acronym/example.sh +18 -0
  12. data/tracks/bash/exercises/armstrong-numbers/README.md +20 -0
  13. data/tracks/bash/exercises/armstrong-numbers/armstrong_numbers_test.sh +58 -0
  14. data/tracks/bash/exercises/armstrong-numbers/example.sh +19 -0
  15. data/tracks/clojure/config.json +8 -0
  16. data/tracks/clojure/exercises/two-fer/README.md +19 -0
  17. data/tracks/clojure/exercises/two-fer/project.clj +4 -0
  18. data/tracks/clojure/exercises/two-fer/src/example.clj +5 -0
  19. data/tracks/clojure/exercises/two-fer/src/two_fer.clj +5 -0
  20. data/tracks/clojure/exercises/two-fer/test/two_fer_test.clj +12 -0
  21. data/tracks/common-lisp/docs/INSTALLATION.md +26 -0
  22. data/tracks/go/exercises/kindergarten-garden/kindergarten_garden_test.go +1 -1
  23. data/tracks/go/exercises/reverse-string/reverse_string_test.go +8 -0
  24. data/tracks/java/config.json +34 -11
  25. data/tracks/java/exercises/armstrong-numbers/.meta/src/reference/java/ArmstrongNumbers.java +20 -0
  26. data/tracks/java/exercises/armstrong-numbers/.meta/version +1 -0
  27. data/tracks/java/exercises/armstrong-numbers/README.md +30 -0
  28. data/tracks/java/exercises/armstrong-numbers/build.gradle +18 -0
  29. data/tracks/java/exercises/armstrong-numbers/src/main/java/ArmstrongNumbers.java +9 -0
  30. data/tracks/java/exercises/armstrong-numbers/src/test/java/ArmstrongNumbersTest.java +80 -0
  31. data/tracks/java/exercises/proverb/.meta/src/reference/java/Proverb.java +19 -0
  32. data/tracks/java/exercises/proverb/README.md +29 -0
  33. data/tracks/java/exercises/proverb/build.gradle +18 -0
  34. data/tracks/java/exercises/proverb/src/main/java/Proverb.java +11 -0
  35. data/tracks/java/exercises/proverb/src/test/java/ProverbTest.java +80 -0
  36. data/tracks/java/exercises/settings.gradle +2 -0
  37. data/tracks/perl6/config.json +1 -1
  38. data/tracks/python/exercises/alphametics/example.py +168 -90
  39. data/tracks/swift/config.json +16 -0
  40. data/tracks/swift/exercises/list-ops/Package.swift +5 -0
  41. data/tracks/swift/exercises/list-ops/README.md +16 -0
  42. data/tracks/swift/exercises/list-ops/Sources/ListOps.swift +1 -0
  43. data/tracks/swift/exercises/list-ops/Sources/ListOpsExample.swift +81 -0
  44. data/tracks/swift/exercises/list-ops/Tests/LinuxMain.swift +6 -0
  45. data/tracks/swift/exercises/list-ops/Tests/ListOpsTests/ListOpsTests.swift +110 -0
  46. metadata +31 -2
@@ -0,0 +1,18 @@
1
+ apply plugin: "java"
2
+ apply plugin: "eclipse"
3
+ apply plugin: "idea"
4
+
5
+ repositories {
6
+ mavenCentral()
7
+ }
8
+
9
+ dependencies {
10
+ testCompile "junit:junit:4.12"
11
+ }
12
+
13
+ test {
14
+ testLogging {
15
+ exceptionFormat = 'full'
16
+ events = ["passed", "failed", "skipped"]
17
+ }
18
+ }
@@ -0,0 +1,9 @@
1
+ class ArmstrongNumbers {
2
+
3
+ boolean isArmstrongNumber(int numberToCheck) {
4
+
5
+ throw new UnsupportedOperationException("Delete this statement and write your own implementation.");
6
+
7
+ }
8
+
9
+ }
@@ -0,0 +1,80 @@
1
+ import org.junit.Before;
2
+ import org.junit.Ignore;
3
+ import org.junit.Test;
4
+
5
+ import static org.junit.Assert.assertTrue;
6
+ import static org.junit.Assert.assertFalse;
7
+
8
+ public class ArmstrongNumbersTest {
9
+
10
+ private ArmstrongNumbers armstrongNumbers;
11
+
12
+ @Before
13
+ public void setup() {
14
+ armstrongNumbers = new ArmstrongNumbers();
15
+ }
16
+
17
+ @Test
18
+ public void singleDigitsAreArmstrongNumbers() {
19
+ int input = 5;
20
+
21
+ assertTrue(armstrongNumbers.isArmstrongNumber(input));
22
+ }
23
+
24
+ @Ignore("Remove to run test")
25
+ @Test
26
+ public void noTwoDigitArmstrongNumbers() {
27
+ int input = 10;
28
+
29
+ assertFalse(armstrongNumbers.isArmstrongNumber(input));
30
+ }
31
+
32
+ @Ignore("Remove to run test")
33
+ @Test
34
+ public void threeDigitNumberIsArmstrongNumber() {
35
+ int input = 153;
36
+
37
+ assertTrue(armstrongNumbers.isArmstrongNumber(input));
38
+ }
39
+
40
+ @Ignore("Remove to run test")
41
+ @Test
42
+ public void threeDigitNumberIsNotArmstrongNumber() {
43
+ int input = 100;
44
+
45
+ assertFalse(armstrongNumbers.isArmstrongNumber(input));
46
+ }
47
+
48
+ @Ignore("Remove to run test")
49
+ @Test
50
+ public void fourDigitNumberIsArmstrongNumber() {
51
+ int input = 9474;
52
+
53
+ assertTrue(armstrongNumbers.isArmstrongNumber(input));
54
+ }
55
+
56
+ @Ignore("Remove to run test")
57
+ @Test
58
+ public void fourDigitNumberIsNotArmstrongNumber() {
59
+ int input = 9475;
60
+
61
+ assertFalse(armstrongNumbers.isArmstrongNumber(input));
62
+ }
63
+
64
+ @Ignore("Remove to run test")
65
+ @Test
66
+ public void sevenDigitNumberIsArmstrongNumber() {
67
+ int input = 9926315;
68
+
69
+ assertTrue(armstrongNumbers.isArmstrongNumber(input));
70
+ }
71
+
72
+ @Ignore("Remove to run test")
73
+ @Test
74
+ public void sevenDigitNumberIsNotArmstrongNumber() {
75
+ int input = 9926314;
76
+
77
+ assertFalse(armstrongNumbers.isArmstrongNumber(input));
78
+ }
79
+
80
+ }
@@ -0,0 +1,19 @@
1
+ final class Proverb {
2
+ private String[] words;
3
+
4
+ Proverb(String[] words) {
5
+ this.words = words;
6
+ }
7
+
8
+ String recite() {
9
+ if (words.length < 1) {
10
+ return "";
11
+ }
12
+ final StringBuilder result = new StringBuilder();
13
+ for(int i = 1; i < words.length; i++) {
14
+ result.append("For want of a " + words[i - 1] + " the " + words[i] + " was lost.\n");
15
+ }
16
+ result.append("And all for the want of a " + words[0] + ".");
17
+ return result.toString();
18
+ }
19
+ }
@@ -0,0 +1,29 @@
1
+ # Proverb in Java
2
+
3
+ For want of a horseshoe nail, a kingdom was lost, or so the saying goes. Dynamically output the full test of this proverbial rhyme.
4
+
5
+ > For want of a nail the shoe was lost.<br>
6
+ > For want of a shoe the horse was lost.<br>
7
+ > For want of a horse the rider was lost.<br>
8
+ > For want of a rider the message was lost.<br>
9
+ > For want of a message the battle was lost.<br>
10
+ > For want of a battle the kingdom was lost.<br>
11
+ > And all for the want of a horseshoe nail.<br>
12
+
13
+ # Running the tests
14
+
15
+ You can run all the tests for an exercise by entering
16
+
17
+ ```sh
18
+ $ gradle test
19
+ ```
20
+
21
+ in your terminal.
22
+
23
+ ## Source
24
+
25
+ Wikipedia [https://en.wikipedia.org/wiki/For_Want_of_a_Nail](https://en.wikipedia.org/wiki/For_Want_of_a_Nail)
26
+
27
+ ## Submitting Incomplete Solutions
28
+
29
+ It's possible to submit an incomplete solution so you can see how others have completed the exercise.
@@ -0,0 +1,18 @@
1
+ apply plugin: "java"
2
+ apply plugin: "eclipse"
3
+ apply plugin: "idea"
4
+
5
+ repositories {
6
+ mavenCentral()
7
+ }
8
+
9
+ dependencies {
10
+ testCompile "junit:junit:4.12"
11
+ }
12
+
13
+ test {
14
+ testLogging {
15
+ exceptionFormat = 'full'
16
+ events = ["passed", "failed", "skipped"]
17
+ }
18
+ }
@@ -0,0 +1,11 @@
1
+ class Proverb {
2
+
3
+ Proverb(String[] words) {
4
+ throw new UnsupportedOperationException("Delete this statement and write your own implementation.");
5
+ }
6
+
7
+ String recite() {
8
+ throw new UnsupportedOperationException("Delete this statement and write your own implementation.");
9
+ }
10
+
11
+ }
@@ -0,0 +1,80 @@
1
+ import org.junit.Ignore;
2
+ import org.junit.Test;
3
+
4
+ import static org.hamcrest.CoreMatchers.is;
5
+ import static org.junit.Assert.assertThat;
6
+
7
+ public class ProverbTest {
8
+
9
+ @Test
10
+ public void zeroWordsAreGiven() {
11
+ String[] words = new String[0];
12
+ String proverb = new Proverb(words).recite(),
13
+ expected = "";
14
+
15
+ assertThat(proverb, is(expected));
16
+ }
17
+
18
+ @Ignore("Remove to run test")
19
+ @Test
20
+ public void singlePieceOfProverb() {
21
+ String[] words = new String[]{"nail"};
22
+ String proverb = new Proverb(words).recite(),
23
+ expected = "And all for the want of a nail.";
24
+
25
+ assertThat(proverb, is(expected));
26
+ }
27
+
28
+ @Ignore("Remove to run test")
29
+ @Test
30
+ public void twoPiecesOfProverb() {
31
+ String[] words = new String[]{"nail", "shoe"};
32
+ String proverb = new Proverb(words).recite(),
33
+ expected = "For want of a nail the shoe was lost.\n" +
34
+ "And all for the want of a nail.";
35
+
36
+ assertThat(proverb, is(expected));
37
+ }
38
+
39
+ @Ignore("Remove to run test")
40
+ @Test
41
+ public void shortChainOfConsequences() {
42
+ String[] words = new String[]{"nail", "shoe", "horse"};
43
+ String proverb = new Proverb(words).recite(),
44
+ expected = "For want of a nail the shoe was lost.\n" +
45
+ "For want of a shoe the horse was lost.\n" +
46
+ "And all for the want of a nail.";
47
+
48
+ assertThat(proverb, is(expected));
49
+ }
50
+
51
+ @Ignore("Remove to run test")
52
+ @Test
53
+ public void fullProverb() {
54
+ String[] words = new String[]{"nail", "shoe", "horse", "rider", "message", "battle", "kingdom"};
55
+ String proverb = new Proverb(words).recite(),
56
+ expected = "For want of a nail the shoe was lost.\n" +
57
+ "For want of a shoe the horse was lost.\n" +
58
+ "For want of a horse the rider was lost.\n" +
59
+ "For want of a rider the message was lost.\n" +
60
+ "For want of a message the battle was lost.\n" +
61
+ "For want of a battle the kingdom was lost.\n" +
62
+ "And all for the want of a nail.";
63
+
64
+ assertThat(proverb, is(expected));
65
+ }
66
+
67
+ @Ignore("Remove to run test")
68
+ @Test
69
+ public void fourPiecesModernizedProverb() {
70
+ String[] words = new String[]{"pin", "gun", "soldier", "battle"};
71
+ String proverb = new Proverb(words).recite(),
72
+ expected = "For want of a pin the gun was lost.\n" +
73
+ "For want of a gun the soldier was lost.\n" +
74
+ "For want of a soldier the battle was lost.\n" +
75
+ "And all for the want of a pin.";
76
+
77
+ assertThat(proverb, is(expected));
78
+ }
79
+
80
+ }
@@ -3,6 +3,7 @@ include 'acronym'
3
3
  include 'all-your-base'
4
4
  include 'allergies'
5
5
  include 'anagram'
6
+ include 'armstrong-numbers'
6
7
  include 'atbash-cipher'
7
8
  include 'bank-account'
8
9
  include 'beer-song'
@@ -57,6 +58,7 @@ include 'pig-latin'
57
58
  include 'poker'
58
59
  include 'prime-factors'
59
60
  include 'protein-translation'
61
+ include 'proverb'
60
62
  include 'pythagorean-triplet'
61
63
  include 'queen-attack'
62
64
  include 'raindrops'
@@ -303,7 +303,7 @@
303
303
  "slug": "roman-numerals",
304
304
  "core": false,
305
305
  "unlocked_by": null,
306
- "difficulty": 3,
306
+ "difficulty": 5,
307
307
  "topics": [
308
308
 
309
309
  ]
@@ -1,98 +1,176 @@
1
- from itertools import permutations
2
- from string import ascii_uppercase as acu
3
- acuset = set(acu)
4
- dset = set(range(10))
5
- nzdset = dset.copy()
6
- nzdset.remove(0)
1
+ from itertools import permutations, chain, product
2
+ """
3
+ This solution will first parse the alphametic expression
4
+ grouping and counting letters buy digit ranks
5
+ then trace recursively all possible permutations starting from
6
+ the lowest rank and genrating additional permutations for new digits
7
+ at higer ranks as necessary.
8
+ This will allow to avoid unnesessarily large permutations to scan.
9
+ Also leading letters in words will be treated as non-zero digits only
10
+ to reduce the number of permutations
11
+ """
7
12
 
8
13
 
9
- def solve(an):
10
- # Break down to words
11
- an = an.upper()
12
- alphaexp = [tuple(map(str.strip, s.split("+")))
13
- for s in an.split("==")]
14
- # Sum powers of 10 for letters ready for computation
15
- expdict = dict()
16
- loexpdict = dict()
17
- for si, s in enumerate(alphaexp):
18
- esign = 1 - (si << 1)
19
- for t in s:
20
- lowletter = t[-1]
21
- if lowletter not in loexpdict:
22
- loexpdict[lowletter] = 0
23
- loexpdict[lowletter] += esign
24
- for p, letter in enumerate(reversed(t)):
25
- if letter not in expdict:
26
- expdict[letter] = 0
27
- expdict[letter] += esign * (10 ** p)
28
-
29
- # Extract all letters and check if they are really letters
30
- alldigits = set(expdict.keys())
31
- if not alldigits <= acuset:
32
- raise ValueError
33
-
34
- # extract high and low digigts
35
- hidigits = set([w[0] for s in alphaexp for w in s])
36
- lodigits = set([w[-1] for s in alphaexp for w in s])
14
+ def digPerms(digset, nzcharset, okzcharset):
15
+ """This function creates permutations given the set of digits,
16
+ letters not alllowed to be 0, and letters allowed to be 0
17
+ """
18
+ nzcnt = len(nzcharset) # How many letters are non-0
19
+ okzcnt = len(okzcharset) # How many letters are allowed 0
20
+ totcnt = nzcnt + okzcnt # Total number of letters
21
+ if totcnt < 1: # if total numbers of letters is 0
22
+ return [()] # return a singe empty permutation
23
+ nzdigset = digset - set((0,)) # generate a non-zero digit set
24
+ nzdigsetcnt = len(nzdigset) # how many non-zero digits are available
25
+ digsetcnt = len(digset) # how many ok zero digits are available
26
+ # if either fewer digits than letters at all or fewer non-0 digits
27
+ # than letters that need to be non-zero
28
+ if digsetcnt < totcnt or nzdigsetcnt < nzcnt:
29
+ return [] # Return no permutations possible
30
+ # Simple case when zeros are allowed everwhere
31
+ # or no zero is containted within the given digits
32
+ elif nzcnt == 0 or digsetcnt == nzdigsetcnt:
33
+ return permutations(digset, totcnt)
34
+ # Another simple case all letters are non-0
35
+ elif okzcnt == 0:
36
+ return permutations(nzdigset, totcnt)
37
+ else:
38
+ # General case
39
+ # Generate a list of possible 0 positions
40
+ poslst = list(range(nzcnt, totcnt))
41
+ # Chain two iterators
42
+ # first iterator with all non-0 permutations
43
+ # second iterator with all permulations without 1 letter
44
+ # insert 0 in all possible positions of that permutation
45
+ return chain(permutations(nzdigset, totcnt),
46
+ map(lambda x: x[0][:x[1]] + (0,) + x[0][x[1]:],
47
+ product(permutations(nzdigset, totcnt - 1),
48
+ poslst)))
37
49
 
38
- # Break down low digits to nonzeros (also high digits) and possible zeros
39
- lonzdigits = lodigits & hidigits
40
- lorestdigits = lodigits - lonzdigits
41
50
 
42
- # Main digits, all but not low
43
- maindigits = alldigits - lodigits
51
+ def check_rec(eqparams, tracecombo=(dict(), 0, set(range(10))), p=0):
52
+ """This function recursively traces a parsed expression from lowest
53
+ digits to highest, generating additional digits when necessary
54
+ checking the digit sum is divisible by 10, carrying the multiple of 10
55
+ up to the next level
56
+ """
57
+ # Basic parameters of the equation,
58
+ # maximal digit rank
59
+ # characters with multipliers by rank
60
+ # unique non-zero characters by rank
61
+ # unique zero-allowed characters by rank
62
+ # all unique characters by rank
63
+ maxp, tchars, unzchars, uokzchars, uchars = eqparams
64
+ # recursion cumulative parameters
65
+ # established characters with digits
66
+ # carry-over from the previous level
67
+ # remaining unassigned digits
68
+ prevdict, cover, remdigs = tracecombo
69
+ # the maximal 10-power (beyond the maximal rank)
70
+ # is reached
71
+ if p == maxp:
72
+ # Carry-over is zero, meaning solution is found
73
+ if cover == 0:
74
+ return prevdict
75
+ else:
76
+ # Otherwise the solution in this branch is not found
77
+ # return empty
78
+ return dict()
79
+ diglets = uchars[p] # all new unique letters from the current level
80
+ partsum = cover # Carry over from lower level
81
+ remexp = [] # TBD letters
82
+ # Break down the current level letter into what can be
83
+ # calculated in the partial sum and remaining TBD letter-digits
84
+ for c, v in tchars[p]:
85
+ if c in prevdict:
86
+ partsum += v * prevdict[c]
87
+ else:
88
+ remexp.append((c, v))
89
+ # Generate permutations for the remaining digits and currecnt level
90
+ # non-zero letters and zero-allowed letters
91
+ for newdigs in digPerms(remdigs, unzchars[p], uokzchars[p]):
92
+ # build the dictionary for the new letters and this level
93
+ newdict = dict(zip(diglets, newdigs))
94
+ # complete the partial sum into test sum using the current permutation
95
+ testsum = partsum + sum([newdict[c] * v
96
+ for c, v in remexp])
97
+ # check if the sum is divisible by 10
98
+ d, r = divmod(testsum, 10)
99
+ if r == 0:
100
+ # if divisible, update the dictionary to all established
101
+ newdict.update(prevdict)
102
+ # proceed to the next level of recursion with
103
+ # the same eqparams, but updated digit dictionary,
104
+ # new carry over and remaining digits to assign
105
+ rectest = check_rec(eqparams,
106
+ (newdict, d, remdigs - set(newdigs)),
107
+ p + 1)
108
+ # if the recursive call returned a non-empty dictionary
109
+ # this means the recursion has found a solution
110
+ # otherwise, proceed to the new permutation
111
+ if len(rectest) > 0:
112
+ return rectest
113
+ # if no permutations are avaialble or no
114
+ # permutation gave the result return the empty dictionary
115
+ return dict()
44
116
 
45
- # Break down main digit list into nonzeroees and possible zeroes
46
- mainnzdigits = maindigits & hidigits
47
- mainrestdigits = maindigits - mainnzdigits
48
117
 
49
- # change sets to tuples to guarantee the stable order
50
- t_lorestdigits = tuple(lorestdigits)
51
- t_lonzdigits = tuple(lonzdigits)
52
- t_lowdigs = t_lorestdigits + t_lonzdigits
53
-
54
- t_mainrestdigits = tuple(mainrestdigits)
55
- t_mainnzdigits = tuple(mainnzdigits)
56
- t_maindigs = t_mainrestdigits + t_mainnzdigits
57
- t_alldigs = t_lowdigs + t_maindigs
118
+ def solve(an):
119
+ """A function to solve the alphametics problem
120
+ """
121
+ # First, split the expresion into left and right parts by ==
122
+ # split each part into words by +
123
+ # strip spaces fro, each word, reverse each work to
124
+ # enumerate the digit rank from lower to higer
125
+ fullexp = [list(map(lambda x: list(reversed(x.strip())), s.split("+")))
126
+ for s in an.strip().upper().split("==")]
127
+ # Find the maximal lenght of the work, maximal possive digit rank or
128
+ # the power of 10, should the < maxp
129
+ maxp = max([len(w) for s in fullexp for w in s])
130
+ # Extract the leading letters for each (reversed) word
131
+ # those cannot be zeros as the number cannot start with 0
132
+ nzchars = set([w[-1] for s in fullexp for w in s])
133
+ # initialize the lists for digit ranks
134
+ unzchars = [] # non-zero letters unique at level
135
+ uokzchars = [] # zero-allowed letters unique at level
136
+ uchars = [] # all letters unique at level
137
+ tchars = [] # all letter with multipliers per level
138
+ for i in range(maxp):
139
+ tchars.append(dict())
140
+ unzchars.append(set())
141
+ uokzchars.append(set())
142
+ # Now lets scan the expression and accumulate the letter counts
143
+ for si, s in enumerate(fullexp):
144
+ sgn = 1 - (si << 1) # left side (0) is +1, right right (1) is -1
145
+ for w in s: # for each word in the side (already reversed)
146
+ for p, c in enumerate(w): # enumerate with ranks
147
+ if c not in tchars[p]: # check if the letter was alread there
148
+ tchars[p][c] = 0
149
+ tchars[p][c] += sgn # append to the rank dictionary
58
150
 
59
- # Check all possible digit permunations with zeros
60
- for lorest in permutations(dset, len(lorestdigits)):
61
- remnzdigs = nzdset - set(lorest)
62
- # Generate addtional non-zero digit permutations
63
- for lonz in permutations(remnzdigs, len(lonzdigits)):
64
- # Build a dictionary for to test the expression
65
- t_digvals = lorest + lonz
66
- # Evaluate the expression sides
67
- testsum = sum([dig * loexpdict[let]
68
- for let, dig in zip(t_lowdigs, t_digvals)])
69
- if testsum % 10 == 0:
70
- # Low digit test passed, check the main digits
71
- # if there are no other digits that low digits,
72
- # test the whole expression and return if OK
73
- if len(maindigits) == 0:
74
- testsum = sum([dig * expdict[let]
75
- for let, dig in zip(t_lowdigs, t_digvals)])
76
- if testsum == 0:
77
- return dict(zip(t_lowdigs, t_digvals))
151
+ totchars = set() # Keep track of letters already seen at lower ranks
152
+ # go through the accumulated rank dictionaries
153
+ for p, chardict in enumerate(tchars):
154
+ for c, cnt in tuple(chardict.items()):
155
+ if cnt == 0: # if the cumulative is 0
156
+ del chardict[c] # remove the letter from check dictionry
157
+ # it does not impact the sum with 0-multiplier
158
+ # if the letter contributes to the sum
159
+ # and was not yet seen at lower ranks
160
+ elif c not in totchars:
161
+ # add the letter to either non-zero set
162
+ # or allowed-zero set
163
+ if c in nzchars:
164
+ unzchars[p].add(c)
78
165
  else:
79
- # non-assigned digits
80
- remdigs = dset - set(t_digvals)
81
- # non-assigned without 0
82
- remnzdigs = remdigs - set((0,))
83
- # permutations for the rest of the digits
84
- for mainrest in permutations(remdigs,
85
- len(t_mainrestdigits)):
86
- lastnzdigs = remnzdigs - set(mainrest)
87
- # permutations for the non-zero rest of the digits
88
- for mainnz in permutations(lastnzdigs,
89
- len(t_mainnzdigits)):
90
- # Evaluate
91
- t_alldigvals = lorest + lonz + mainrest + mainnz
92
- testsum = sum([dig * expdict[let]
93
- for let, dig in zip(t_alldigs,
94
- t_alldigvals)])
95
- if testsum == 0:
96
- return dict(zip(t_alldigs, t_alldigvals))
97
-
98
- return {}
166
+ uokzchars[p].add(c)
167
+ # add to the list as seen letter to ignore at the next
168
+ # ranks
169
+ totchars.add(c)
170
+ # pre-build the combo list of letters for the rank
171
+ # non-zero first, followed by zero-allowed
172
+ uchars.append(tuple(unzchars[p]) + tuple(uokzchars[p]))
173
+ # pre-convert check dictionaries to tuples
174
+ tchars[p] = tuple(chardict.items())
175
+ # go for the recursion
176
+ return check_rec([maxp, tchars, unzchars, uokzchars, uchars])