ref: 2183f9eaeca9fb5d57b637c15707e663caf33575
dir: /appl/math/powers.b/
implement Powers;
include "sys.m";
sys: Sys;
include "draw.m";
include "arg.m";
include "lock.m";
lockm: Lock;
Semaphore: import lockm;
Powers: module
{
init: fn(nil: ref Draw->Context, nil: list of string);
};
MAXNODES: con (1<<20)/4;
verbose: int;
# Doing
# powers -p 3
# gives
# [2] 1729 = 1**3 + 12**3 = 9**3 + 10**3
# [2] 4104 = 2**3 + 16**3 = 9**3 + 15**3
# ie 1729 can be written in two ways as the sum of 2 cubes as can 4104.
# The options are
# -p the power to use - default 2
# -n the number of powers summed - default 2
# -f the minimum number of ways found before reporting it - default 2
# -l the least number to consider - default 0
# -m the greatest number to consider - default 8192
# Thus
# pow -p 4 -n 3 -f 3 -l 0 -m 1000000
# gives
# [3] 811538 = 12**4 + 17**4 + 29**4 = 7**4 + 21**4 + 28**4 = 4**4 + 23**4 + 27**4
# ie fourth powers, 3 in each sum, minimum of 3 representations, numbers from 0-1000000.
# [2] 25
# [3] 325
# [4] 1105
# [5] 4225
# [6] 5525
# [7] 203125
# [8] 27625
# [9] 71825
# [10] 138125
# [11] 2640625
# [12] 160225
# [13] 17850625
# [14] 1221025
# [15] 1795625
# [16] 801125
# [18] 2082925
# [20] 4005625
# [23] 30525625
# [24] 5928325
# [32] 29641625
# [24] 5928325 = 63**2 + 2434**2 = 94**2 + 2433**2 = 207**2 + 2426**2 = 294**2 + 2417**2 = 310**2 + 2415**2 = 465**2 + 2390**2 = 490**2 + 2385**2 = 591**2 + 2362**2 = 690**2 + 2335**2 = 742**2 + 2319**2 = 849**2 + 2282**2 = 878**2 + 2271**2 = 959**2 + 2238**2 = 1039**2 + 2202**2 = 1062**2 + 2191**2 = 1201**2 + 2118**2 = 1215**2 + 2110**2 = 1290**2 + 2065**2 = 1410**2 + 1985**2 = 1454**2 + 1953**2 = 1535**2 + 1890**2 = 1614**2 + 1823**2 = 1633**2 + 1806**2 = 1697**2 + 1746**2
# [32] 29641625 = 67**2 + 5444**2 = 124**2 + 5443**2 = 284**2 + 5437**2 = 320**2 + 5435**2 = 515**2 + 5420**2 = 584**2 + 5413**2 = 835**2 + 5380**2 = 955**2 + 5360**2 = 1180**2 + 5315**2 = 1405**2 + 5260**2 = 1460**2 + 5245**2 = 1648**2 + 5189**2 = 1795**2 + 5140**2 = 1829**2 + 5128**2 = 1979**2 + 5072**2 = 2012**2 + 5059**2 = 2032**2 + 5051**2 = 2245**2 + 4960**2 = 2308**2 + 4931**2 = 2452**2 + 4861**2 = 2560**2 + 4805**2 = 2621**2 + 4772**2 = 2840**2 + 4645**2 = 3005**2 + 4540**2 = 3035**2 + 4520**2 = 3320**2 + 4315**2 = 3365**2 + 4280**2 = 3517**2 + 4156**2 = 3544**2 + 4133**2 = 3664**2 + 4027**2 = 3715**2 + 3980**2 = 3803**2 + 3896**2
# [2] 1729 = 1**3 + 12**3 = 9**3 + 10**3
# [2] 4104 = 2**3 + 16**3 = 9**3 + 15**3
# [3] 87539319 = 167**3 + 436**3 = 228**3 + 423**3 = 255**3 + 414**3
# [2] 635318657 = 59**4 + 158**4 = 133**4 + 134**4
# [2] 3262811042 = 7**4 + 239**4 = 157**4 + 227**4
# [2] 8657437697 = 193**4 + 292**4 = 256**4 + 257**4
# [2] 68899596497 = 271**4 + 502**4 = 298**4 + 497**4
# [2] 86409838577 = 103**4 + 542**4 = 359**4 + 514**4
# [2] 160961094577 = 222**4 + 631**4 = 503**4 + 558**4
# [2] 2094447251857 = 76**4 + 1203**4 = 653**4 + 1176**4
# [2] 4231525221377 = 878**4 + 1381**4 = 997**4 + 1342**4
# [2] 26033514998417 = 1324**4 + 2189**4 = 1784**4 + 1997**4
# [2] 37860330087137 = 1042**4 + 2461**4 = 2026**4 + 2141**4
# [2] 61206381799697 = 248**4 + 2797**4 = 2131**4 + 2524**4
# [2] 76773963505537 = 1034**4 + 2949**4 = 1797**4 + 2854**4
# [2] 109737827061041 = 1577**4 + 3190**4 = 2345**4 + 2986**4
# [2] 155974778565937 = 1623**4 + 3494**4 = 2338**4 + 3351**4
# [2] 156700232476402 = 661**4 + 3537**4 = 2767**4 + 3147**4
# [2] 621194785437217 = 2694**4 + 4883**4 = 3966**4 + 4397**4
# [2] 652057426144337 = 604**4 + 5053**4 = 1283**4 + 5048**4
# [2] 680914892583617 = 3364**4 + 4849**4 = 4288**4 + 4303**4
# [2] 1438141494155441 = 2027**4 + 6140**4 = 4840**4 + 5461**4
# [2] 1919423464573697 = 274**4 + 6619**4 = 5093**4 + 5942**4
# [2] 2089568089060657 = 498**4 + 6761**4 = 5222**4 + 6057**4
# [2] 2105144161376801 = 2707**4 + 6730**4 = 3070**4 + 6701**4
# [2] 3263864585622562 = 1259**4 + 7557**4 = 4661**4 + 7269**4
# [2] 4063780581008977 = 5181**4 + 7604**4 = 6336**4 + 7037**4
# [2] 6315669699408737 = 1657**4 + 8912**4 = 7432**4 + 7559**4
# [2] 6884827518602786 = 635**4 + 9109**4 = 3391**4 + 9065**4
# [2] 7191538859126257 = 4903**4 + 9018**4 = 6842**4 + 8409**4
# [2] 7331928977565937 = 1104**4 + 9253**4 = 5403**4 + 8972**4
# [2] 7362748995747617 = 5098**4 + 9043**4 = 6742**4 + 8531**4
# [2] 7446891977980337 = 1142**4 + 9289**4 = 4946**4 + 9097**4
# [2] 7532132844821777 = 173**4 + 9316**4 = 4408**4 + 9197**4
# [2] 7985644522300177 = 6262**4 + 8961**4 = 7234**4 + 8511**4
# 5, 6, 7, 8, 9, 10, 11 none
Btree: adt{
sum: big;
left: cyclic ref Btree;
right: cyclic ref Btree;
};
Dtree: adt{
sum: big;
freq: int;
lst: list of array of int;
left: cyclic ref Dtree;
right: cyclic ref Dtree;
};
nCr(n: int, r: int): int
{
if(r > n-r)
r = n-r;
# f := g := 1;
# for(i := 0; i < r; i++){
# f *= n-i;
# g *= i+1;
# }
# return f/g;
num := array[r] of int;
den := array[r] of int;
for(i := 0; i < r; i++){
num[i] = n-i;
den[i] = i+1;
}
for(i = 0; i < r; i++){
for(j := 0; den[i] != 1; j++){
if(num[j] == 1)
continue;
k := hcf(num[j], den[i]);
if(k != 1){
num[j] /= k;
den[i] /= k;
}
}
}
f := 1;
for(i = 0; i < r; i++)
f *= num[i];
return f;
}
nHr(n: int, r: int): int
{
if(n == 0)
return 0;
return nCr(n+r-1, r);
}
nSr(n: int, i: int, j: int): int
{
return nHr(j, n)-nHr(i, n);
# s := 0;
# for(k := i; k < j; k++)
# s += nHr(k+1, n-1);
# return s;
}
nSrmax(n: int, i: int, m: int): int
{
s := 0;
for(k := i; ; k++){
s += nHr(k+1, n-1);
if(s > m)
break;
}
if(k == i)
return i+1;
return k;
}
kth(c: array of int, n: int, i: int, j: int, k: int)
{
l, u: int;
m := nSr(n, i, j);
if(k < 0)
k = 0;
if(k >= m)
k = m-1;
p := 0;
for(q := 0; q < n; q++){
if(q == 0){
l = i;
u = j-1;
}
else{
l = 0;
u = c[q-1];
}
for(x := l; x <= u; x++){
m = nHr(x+1, n-q-1);
p += m;
if(p > k){
p -= m;
break;
}
}
c[q] = x;
}
}
pos(c: array of int, n: int): int
{
p := 0;
for(q := 0; q < n; q++)
p += nSr(n-q, 0, c[q]);
return p;
}
min(c: array of int, n: int, p: int): big
{
s := big(0);
for(i := 0; i < n; i++)
s += big(c[i])**p;
m := s;
for(i = n-1; i > 0; i--){
s -= big(c[i])**p;
s -= big(c[i-1])**p;
c[i]--;
c[i-1]++;
s += big(c[i-1])**p;
if(s < m)
m = s;
}
c[0]--;
c[n-1]++;
# m--;
return m;
}
hcf(a, b: int): int
{
if(b == 0)
return a;
for(;;){
if(a == 0)
break;
if(a < b)
(a, b) = (b, a);
a %= b;
# a -= (a/b)*b;
}
return b;
}
gcd(l: list of array of int): int
{
g := (hd l)[0];
for(; l != nil; l = tl l){
d := hd l;
n := len d;
for(i := 0; i < n; i++)
g = hcf(d[i], g);
}
return g;
}
adddup(s: big, root: ref Dtree): int
{
n, p, lp: ref Dtree;
p = root;
while(p != nil){
if(s == p.sum)
return ++p.freq;
lp = p;
if(s < p.sum)
p = p.left;
else
p = p.right;
}
n = ref Dtree(s, 2, nil, nil, nil);
if(s < lp.sum)
lp.left = n;
else
lp.right = n;
return n.freq;
}
cp(c: array of int): array of int
{
n := len c;
m := 0;
for(i := 0; i < n; i++)
if(c[i] != 0)
m++;
nc := array[m] of int;
nc[0: ] = c[0: m];
return nc;
}
finddup(s: big, c: array of int, root: ref Dtree, f: int)
{
p: ref Dtree;
p = root;
while(p != nil){
if(s == p.sum){
if(p.freq >= f)
p.lst = cp(c) :: p.lst;
return;
}
if(s < p.sum)
p = p.left;
else
p = p.right;
}
}
printdup(p: ref Dtree, pow: int, ix: int)
{
if(p == nil)
return;
printdup(p.left, pow, ix);
if((l := p.lst) != nil){
if(gcd(l) == 1){
min1 := min2 := 16r7fffffff;
for(; l != nil; l = tl l){
n := len hd l;
if(n < min1){
min2 = min1;
min1 = n;
}
else if(n < min2)
min2 = n;
}
i := min1+min2-pow;
if(i <= ix){
sys->print("[%d, %d] %bd", i, p.freq, p.sum);
for(l = p.lst; l != nil; l = tl l){
d := hd l;
n := len d;
sys->print(" = ");
for(j := n-1; j >= 0; j--){
sys->print("%d**%d", d[j], pow);
if(j > 0)
sys->print(" + ");
}
}
sys->print("\n");
if(i < 0){
sys->print("****************\n");
exit;
}
}
}
}
printdup(p.right, pow, ix);
}
addsum(s: big, root: ref Btree, root1: ref Dtree): int
{
n, p, lp: ref Btree;
p = root;
while(p != nil){
if(s == p.sum)
return adddup(s, root1);
lp = p;
if(s < p.sum)
p = p.left;
else
p = p.right;
}
n = ref Btree(s, nil, nil);
if(s < lp.sum)
lp.left = n;
else
lp.right = n;
return 1;
}
oiroot(x: big, p: int): int
{
for(i := 0; ; i++){
n := big(i)**p;
if(n > x)
break;
}
return i-1;
}
iroot(x: big, p: int): int
{
m: big;
if(x == big(0) || x == big(1))
return int x;
v := x;
n := 0;
for(i := 32; i > 0; i >>= 1){
m = ((big(1)<<i)-big(1))<<i;
if((v&m) != big(0)){
n += i;
v >>= i;
}
}
a := big(1) << (n/p);
b := a<<1;
while(a < b){
m = (a+b+big(1))/big(2);
y := m**p;
if(y > x)
b = m-big(1);
else if(y < x)
a = m;
else
a = b = m;
}
if(a**p <= x && (a+big(1))**p > x)
;
else{
sys->print("fatal: %bd %d -> %bd\n", x, p, a);
exit;
}
return int a;
}
initval(c: array of int, n: int, p: int, v: int): big
{
for(i := 0; i < n; i++)
c[i] = 0;
c[0] = v;
return big(v)**p;
}
nxtval(c: array of int, n: int, p: int, s: big): big
{
for(k := n-1; k >= 0; k--){
s -= big(c[k])**p;
c[k]++;
if(k == 0){
s += big(c[k])**p;
break;
}
else{
if(c[k] <= c[k-1]){
s += big(c[k])**p;
break;
}
c[k] = 0;
}
}
return s;
}
powers(p: int, n: int, f: int, ix: int, lim0: big, lim: big, ch: chan of int, lock: ref Semaphore)
{
root := ref Btree(big(-1), nil, nil);
root1 := ref Dtree(big(-1), 0, nil, nil, nil);
min := max := lim0;
c := array[n] of int;
for(;;){
imin := iroot((min+big(n-1))/big(n), p);
imax := nSrmax(n, imin, MAXNODES);
max = big(imax)**p - big(1);
while(max <= min){ # could do better
imax++;
max = big(imax)**p - big(1);
}
if(max > lim){
max = lim;
imax = iroot(max, p)+1;
}
if(verbose)
sys->print("searching in %d-%d(%bd-%bd)\n", imin, imax, min, max);
m := mm := 0;
maxf := 0;
s := initval(c, n, p, imin);
for(;;){
mm++;
if(s >= min && s < max){
fr := addsum(s, root, root1);
if(fr > maxf)
maxf = fr;
m++;
}
s = nxtval(c, n, p, s);
if(c[0] == imax)
break;
}
root.left = root.right = nil;
if(maxf >= f){
if(verbose)
sys->print("finding duplicates\n");
s = initval(c, n, p, imin);
for(;;){
if(s >= min && s < max)
finddup(s, c, root1, f);
s = nxtval(c, n, p, s);
if(c[0] == imax)
break;
}
if(lock != nil)
lock.obtain();
printdup(root1, p, ix);
if(lock != nil)
lock.release();
root1.left = root1.right = nil;
}
if(verbose)
sys->print("%d(%d) nodes searched\n", m, mm);
if(mm != nSr(n, imin, imax)){
sys->print("**fatal**\n");
exit;
}
min = max;
if(min >= lim)
break;
}
if(ch != nil)
ch <-= 0;
}
usage()
{
sys->print("usage: powers -p power -n number -f frequency -i index -l minimum -m maximum -s procs -v\n");
exit;
}
partition(p: int, n: int, l: big, m: big, s: int): array of big
{
a := array[s+1] of big;
a[0] = big(iroot(l, p))**n;
a[s] = (big(iroot(m, p))+big(1))**n;
nn := a[s]-a[0];
q := nn/big(s);
r := nn-q*big(s);
t := big(0);
lb := a[0];
for(i := 0; i < s; i++){
ub := lb+q;
t += r;
if(t >= big(s)){
ub++;
t -= big(s);
}
a[i+1] = ub;
lb = ub;
}
if(a[s] != a[0]+nn){
sys->print("fatal: a[s]\n");
exit;
}
for(i = 0; i < s; i++){
# sys->print("%bd %bd\n", a[i], a[i]**p);
a[i] = big(iroot(a[i], n))**p;
}
a[0] = l;
a[s] = m;
while(a[0] >= a[1]){
a[1] = a[0];
a = a[1: ];
--s;
}
while(a[s] <= a[s-1]){
a[s-1] = a[s];
a = a[0: s];
--s;
}
return a;
}
init(nil: ref Draw->Context, args: list of string)
{
sys = load Sys Sys->PATH;
arg := load Arg Arg->PATH;
lockm = load Lock Lock->PATH;
lockm->init();
lock := Semaphore.new();
p := n := f := 2;
ix := 1<<30;
l := m := big(0);
s := 1;
arg->init(args);
while((c := arg->opt()) != 0){
case c {
'p' =>
p = int arg->arg();
'n' =>
n = int arg->arg();
'f' =>
f = int arg->arg();
'i' =>
ix = int arg->arg();
'l' =>
l = big(arg->arg());
'm' =>
m = big(arg->arg())+big(1);
's' =>
s = int arg->arg();
'v' =>
verbose = 1;
* =>
usage();
}
}
if(arg->argv() != nil)
usage();
if(p < 2){
p = 2;
sys->print("setting p = %d\n", p);
}
if(n < 2){
n = 2;
sys->print("setting n = %d\n", n);
}
if(f < 2){
f = 2;
sys->print("setting f = %d\n", f);
}
if(l < big(0)){
l = big(0);
sys->print("setting l = %bd\n", l);
}
if(m <= big(0)){
m = big((1<<13)+1);
sys->print("setting m = %bd\n", m-big(1));
}
if(l >= m)
exit;
if(s <= 1)
powers(p, n, f, ix, l, m, nil, nil);
else{
nproc := 0;
ch := chan of int;
a := partition(p, n, l, m, s);
lb := a[0];
for(i := 0; i < s; i++){
ub := a[i+1];
if(lb < ub){
nproc++;
spawn powers(p, n, f, ix, lb, ub, ch, lock);
}
lb = ub;
}
for( ; nproc != 0; nproc--)
<- ch;
}
}