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.
- checksums.yaml +4 -4
- data/lib/trackler/version.rb +1 -1
- data/problem-specifications/exercises/beer-song/canonical-data.json +33 -17
- data/problem-specifications/exercises/binary-search/canonical-data.json +41 -21
- data/problem-specifications/exercises/book-store/canonical-data.json +58 -30
- data/problem-specifications/exercises/two-fer/metadata.yml +1 -1
- data/tracks/bash/config.json +35 -13
- data/tracks/bash/exercises/acronym/README.md +27 -0
- data/tracks/bash/exercises/acronym/acronym.sh +18 -0
- data/tracks/bash/exercises/acronym/acronym_tests.sh +43 -0
- data/tracks/bash/exercises/acronym/example.sh +18 -0
- data/tracks/bash/exercises/armstrong-numbers/README.md +20 -0
- data/tracks/bash/exercises/armstrong-numbers/armstrong_numbers_test.sh +58 -0
- data/tracks/bash/exercises/armstrong-numbers/example.sh +19 -0
- data/tracks/clojure/config.json +8 -0
- data/tracks/clojure/exercises/two-fer/README.md +19 -0
- data/tracks/clojure/exercises/two-fer/project.clj +4 -0
- data/tracks/clojure/exercises/two-fer/src/example.clj +5 -0
- data/tracks/clojure/exercises/two-fer/src/two_fer.clj +5 -0
- data/tracks/clojure/exercises/two-fer/test/two_fer_test.clj +12 -0
- data/tracks/common-lisp/docs/INSTALLATION.md +26 -0
- data/tracks/go/exercises/kindergarten-garden/kindergarten_garden_test.go +1 -1
- data/tracks/go/exercises/reverse-string/reverse_string_test.go +8 -0
- data/tracks/java/config.json +34 -11
- data/tracks/java/exercises/armstrong-numbers/.meta/src/reference/java/ArmstrongNumbers.java +20 -0
- data/tracks/java/exercises/armstrong-numbers/.meta/version +1 -0
- data/tracks/java/exercises/armstrong-numbers/README.md +30 -0
- data/tracks/java/exercises/armstrong-numbers/build.gradle +18 -0
- data/tracks/java/exercises/armstrong-numbers/src/main/java/ArmstrongNumbers.java +9 -0
- data/tracks/java/exercises/armstrong-numbers/src/test/java/ArmstrongNumbersTest.java +80 -0
- data/tracks/java/exercises/proverb/.meta/src/reference/java/Proverb.java +19 -0
- data/tracks/java/exercises/proverb/README.md +29 -0
- data/tracks/java/exercises/proverb/build.gradle +18 -0
- data/tracks/java/exercises/proverb/src/main/java/Proverb.java +11 -0
- data/tracks/java/exercises/proverb/src/test/java/ProverbTest.java +80 -0
- data/tracks/java/exercises/settings.gradle +2 -0
- data/tracks/perl6/config.json +1 -1
- data/tracks/python/exercises/alphametics/example.py +168 -90
- data/tracks/swift/config.json +16 -0
- data/tracks/swift/exercises/list-ops/Package.swift +5 -0
- data/tracks/swift/exercises/list-ops/README.md +16 -0
- data/tracks/swift/exercises/list-ops/Sources/ListOps.swift +1 -0
- data/tracks/swift/exercises/list-ops/Sources/ListOpsExample.swift +81 -0
- data/tracks/swift/exercises/list-ops/Tests/LinuxMain.swift +6 -0
- data/tracks/swift/exercises/list-ops/Tests/ListOpsTests/ListOpsTests.swift +110 -0
- 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,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'
|
data/tracks/perl6/config.json
CHANGED
@@ -1,98 +1,176 @@
|
|
1
|
-
from itertools import permutations
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
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
|
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
|
-
|
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
|
-
|
43
|
-
|
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
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
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
|
-
#
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
#
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
#
|
71
|
-
|
72
|
-
|
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
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
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])
|