aboutsummaryrefslogtreecommitdiff
path: root/src/algebra/numtheor.spad.pamphlet
blob: 4a8101ea9a91e1f971a880823a8c29f3b9bf9c39 (plain)
1
2
3
4
5
6
7
8
9
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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
\documentclass{article}
\usepackage{open-axiom}
\begin{document}
\title{\$SPAD/src/algebra numtheor.spad}
\author{Martin Brock, Timothy Daly, Michael Monagan, Robert Sutor, 
Clifton J. Williamson}
\maketitle
\begin{abstract}
\end{abstract}
\eject
\tableofcontents
\eject
\section{package INTHEORY IntegerNumberTheoryFunctions}
\subsection{The inverse function}
The inverse function is derived from the 
{\bf Extended Euclidean Algorithm}.
If we divide one integer by another nonzero integer we get an integer
quotient plus a remainder which is, in general, a rational number. 
For instance, 
\[13/5 = 2 + 3/5\]
where 2 is the quotient and $3/5$ is the remainder.

If we multiply thru by the denominator of the remainder we get an answer
in integer terms which no longer involves division:
\[13 = 2(5) + 3\]

This gives a method of dividing integers. Specifically, if a and b are
positive integers, there exist unique non-negative integers q
and r so that
\[a = qb + r , {\rm\ where\ } 0 \le r < b\]
q is called the quotient and r the remainder.

The greatest common divisor of integers a and b, denoted by gcd(a,b),
is the largest integer that divides (without remainder) both a and
b. So, for example: gcd(15, 5) = 5, gcd(7, 9) = 1, gcd(12, 9) = 3,
gcd(81, 57) = 3.

The gcd of two integers can be found by repeated application of the
division algorithm, this is known as the Euclidean Algorithm. You
repeatedly divide the divisor by the remainder until the remainder is
0. The gcd is the last non-zero remainder in this algorithm. The
following example shows the algorithm.

Finding the gcd of 81 and 57 by the {\bf Euclidean Algorithm}:
\[
\begin{array}{rcl}
81 & = & 1(57) + 24\\
57 & = & 2(24) + 9\\
24 & = & 2(9) + 6\\
9 & = & 1(6) + 3\\
6 & = & 2(3) + 0
\end{array}
\]

So the greatest commmon divisor, the GCD(81,51)=3.

If the gcd(a, b) = r then there exist integers s and t so that
\[s(a) + t(b) = r\]

By back substitution in the steps in the Euclidean Algorithm, it is
possible to find these integers s and t. We shall do this with the
above example:

Starting with the next to last line, we have:
\[3 = 9 -1(6)\]
From the line before that, we see that $6 = 24 - 2(9)$, so:
\[3 = 9 - 1(24 - 2(9)) = 3(9) - 1(24)\]
From the line before that, we have $9 = 57 - 2(24)$, so:
\[3 = 3( 57 - 2(24)) - 1(24) = 3(57) - 7(24)\]
And, from the line before that $24 = 81 - 1(57)$, giving us:
\[3 = 3(57) - 7( 81 - 1(57)) = 10(57) -7(81)\]
So we have found s = -7 and t = 10.

The {\bf Extended Euclidean Algorithm} computes the GCD($a$,$b$) and 
the values for $s$ and $t$.

Suppose we were doing arithmetics modulo 26 and we needed to find the
inverse of a number mod 26. This turned out to be a difficult task
(and not always possible). We observed that a number $x$ had an inverse
mod 26 (i.e., a number $y$ so that $xy = 1 {\rm\ mod\ } 26$) 
if and only if $gcd(x,26) = 1$. 
In the general case the inverse
of $x$ exists if and only if $gcd(x, n) = 1$ and if it exists then
there exist integers $s$ and $t$ so that

\[sx + tn = 1\]

But this says that $sx = 1 + (-t)n$, or in other words, 
\[sx \equiv 1 {\rm\ mod\ } n\]  
So, s (reduced mod n if need be) is the inverse of $x {\rm\ mod\ }n$. 
The extended Euclidean algorithm calculates $s$ efficiently.

\subsubsection{Finding the inverse mod n}

We will number the steps of the Euclidean algorithm starting
with step 0. The quotient obtained at step $i$ will be denoted by $q_i$ 
and an auxillary number, $s_i$. For the first two steps, the value
of this number is given: $s_0 = 0$ and $s_1 = 1$. For the remainder of the
steps, we recursively calculate 
\[s_i = s_{i-2} - s_{i-1} q_{i-2} {\rm\ (mod\ n)}\] 

The algorithm starts by "dividing" $n$ by $x$. 
If the last non-zero remainder occurs at step $k$, 
then if this remainder is 1, $x$ has an inverse and it is $s_{k+2}$. 
If the remainder is not 1, then $x$ does not have an inverse. 

Find the inverse of 15 mod 26.
\[
\begin{array}{crcll}
Step 0: &26 &=& 1(15) + 11 &s_0 = 0\\
Step 1: &15 &=& 1(11) + 4  &s_1 = 1\\
Step 2: &11 &=& 2(4) + 3   
&s_2 = 0 - 1( 1) {\rm\ mod\ } 26 = 25\\
Step 3: &4  &=& 1(3) + 1   
&s_3 = 1 - 25( 1) {\rm\ mod\ } 26 = -24 {\rm\ mod\ } 26 = 2\\
Step 4: &3  &=& 3(1) + 0   
&s_4 = 25 - 2( 2) {\rm\ mod\ } 26 = 21\\
&&& &s_5 = 2 - 21( 1) {\rm\ mod\ } 26 = -19 {\rm\ mod\ } 26 = 7
\end{array}
\]
Notice that $15(7) = 105 = 1 + 4(26) \equiv 1 ({\rm\ mod\ } 26)$. 

Using the half extended Euclidean algorithm we compute 1/a mod b.
<<inverse(a,b)>>=
  inverse(a,b) ==
    borg:I:=b
    c1:I := 1
    d1:I := 0
    while b ~= 0 repeat
      q:I := a quo b
      r:I := a-q*b
      (a,b):=(b,r)
      (c1,d1):=(d1,c1-q*d1)
    not one? a => error("moduli are not relatively prime")
    positiveRemainder(c1,borg)
  
@
<<inverse : (I,I) -> I>>=
  inverse : (I,I) -> I
@
Since this algorithm in local we need to reproduce it in the 
input file for testing purposes.
<<TESTinverse>>=
)clear completely 

inverse:(INT,INT)->INT

inverse(a,b) ==
  borg:INT:=b
  c1:INT := 1
  d1:INT := 0
  while b ~= 0 repeat
    q := a quo b
    r := a-q*b
    print [a, "=", q, "*(", b, ")+", r]
    (a,b):=(b,r)
    (c1,d1):=(d1,c1-q*d1)
  not one? a => error("moduli are not relatively prime")
  positiveRemainder(c1,borg)

if not one?((inverse(26,15)*26)::IntegerMod(15)) then print "DALY BUG"
if not one?((inverse(15,26)*15)::IntegerMod(26)) then print "DALY BUG"

@
\subsection{The Chinese Remainder Algorithm}
\subsubsection{Chinese Remainder Theorem}
Let $m_1$,$m_2$,\ldots,$m_r$ be positive integers that are pairwise
relatively prime. Let $x_1$,$x_2$,\ldots,$x_r$ be integers with 
$0 \le x_i < m_i$. Then, there is exactly one $x$ in the interval
\[0 \le x < m_1 \cdot m_2 \cdots m_r\]
that satisfies the remainder equations
\[{\rm\ irem\ }(x,m_i) = x_i,\ \ \ i=1,2,\ldots,r\]
where {\bf irem} is the positive integer remainder function.
\subsubsection{Chinese Remainder Example}
Let $x_1 = 4$, $m_1 = 5$, $x_2 = 2$, $m_2 = 3$. We know that 
\[{\rm\ irem\ }(x,m_1) = x_1\]
\[{\rm\ irem\ }(x,m_2) = x_2\]
where $0 \le x_1 < m_1$ and $0 \le x_2 < m_2$. By the extended
Euclidean Algorithm there are integers $c$ and $d$ such that
\[c m_1 + d m_2 = 1\].
\noindent
In this case we are looking for an integer such that
\[{\rm\ irem\ }(x,5) = 4\]
\[{\rm\ irem\ }(x,3) = 2\]

The algorithm we use is to first 
compute the positive integer
remainder of $x_1$ and $m_1$ to get a new $x_1$:
\[
\begin{array}{rcl}
x_1 & = & {\rm\ positiveRemainder\ }(x_1,m_1)\\
4 & = & {\rm\ positiveRemainder\ }(4,5)
\end{array}
\]
Next compute the positive integer
remainder of $x_2$ and $m_2$ to get a new $x_2$:
\[
\begin{array}{rcl}
x_2 & = & {\rm\ positiveRemainder\ }(x_2,m_2)\\
2 & = & {\rm\ positiveRemainder\ }(2,3)
\end{array}
\]
Then we compute
\[x_1 + m_1 \cdot {\rm\ positiveRemainder\ }
(((x_2-x_1)\cdot{\rm inverse}(m_1,m_2)),m_2)\]
or
\[4+5*{\rm\ positiveRemainder\ }(((2-4)*{\rm\ inverse\ }(5,3)),3)\]
or
\[4+5*{\rm\ positiveRemainder\ }(-2*2),3)\]
or
\[4+5*2\]
or
\[14\]
<<chineseRemainder(x1,m1,x2,m2)>>=
  chineseRemainder(x1,m1,x2,m2) ==
    negative? m1 or negative? m2 => error "moduli must be positive"
    x1 := positiveRemainder(x1,m1)
    x2 := positiveRemainder(x2,m2)
    x1 + m1 * positiveRemainder(((x2-x1) * inverse(m1,m2)),m2)

@
This function has a restricted signature which only allows for
computing the chinese remainder of two numbers and two moduli.
<<chineseRemainder: (I,I,I,I) -> I>>=
  chineseRemainder: (I,I,I,I) -> I
    ++ \spad{chineseRemainder(x1,m1,x2,m2)} returns w, where w is such that
    ++ \spad{w = x1 mod m1} and \spad{w = x2 mod m2}. Note: \spad{m1} and
    ++ \spad{m2} must be relatively prime.
@
We test the particular example. The result should be 14.
<<TESTchineseRemainder>>=
)clear all
x1:=4
m1:=5
x2:=2
m2:=3
result:=chineseRemainder(x1,m1,x2,m2)
expected:=14
if (result-expected ~=0) then print "DALY BUG"

@
<<package INTHEORY IntegerNumberTheoryFunctions>>=
)abbrev package INTHEORY IntegerNumberTheoryFunctions
++ Author: Michael Monagan, Martin Brock, Robert Sutor, Timothy Daly
++ Date Created: June 1987
++ Date Last Updated:
++ Basic Operations:
++ Related Domains:
++ Also See:
++ AMS Classifications:
++ Keywords: number theory, integer
++ Examples:
++ References: Knuth, The Art of Computer Programming Vol.2
++ Description:
++ This package provides various number theoretic functions on the integers.
IntegerNumberTheoryFunctions(): Exports == Implementation where
 I ==> Integer
 RN ==> Fraction I
 SUP ==> SparseUnivariatePolynomial
 NNI ==> NonNegativeInteger

 Exports ==> with
  bernoulli : I -> RN
    ++ \spad{bernoulli(n)} returns the nth Bernoulli number.
    ++ this is \spad{B(n,0)}, where \spad{B(n,x)} is the \spad{n}th Bernoulli
    ++ polynomial.
<<chineseRemainder: (I,I,I,I) -> I>>
  divisors : I -> List I
    ++ \spad{divisors(n)} returns a list of the divisors of n.
  euler : I -> I
    ++ \spad{euler(n)} returns the \spad{n}th Euler number. This is
    ++ \spad{2^n E(n,1/2)}, where \spad{E(n,x)} is the nth Euler polynomial.
  eulerPhi : I -> I
    ++ \spad{eulerPhi(n)} returns the number of integers between 1 and n
    ++ (including 1) which are relatively prime to n. This is the Euler phi
    ++ function \spad{\phi(n)} is also called the totient function.
  fibonacci : I -> I
    ++ \spad{fibonacci(n)} returns the nth Fibonacci number. the Fibonacci
    ++ numbers \spad{F[n]} are defined by \spad{F[0] = F[1] = 1} and
    ++ \spad{F[n] = F[n-1] + F[n-2]}.
    ++ The algorithm has running time \spad{O(log(n)^3)}.
    ++ Reference: Knuth, The Art of Computer Programming
    ++ Vol 2, Semi-Numerical Algorithms.
  harmonic : I -> RN
    ++ \spad{harmonic(n)} returns the nth harmonic number. This is
    ++ \spad{H[n] = sum(1/k,k=1..n)}.
  jacobi : (I,I) -> I
    ++ \spad{jacobi(a,b)} returns the Jacobi symbol \spad{J(a/b)}.
    ++ When b is odd, \spad{J(a/b) = product(L(a/p) for p in factor b )}.
    ++ Note: by convention, 0 is returned if \spad{gcd(a,b) ~= 1}.
    ++ Iterative \spad{O(log(b)^2)} version coded by Michael Monagan June 1987.
  legendre : (I,I) -> I
    ++ \spad{legendre(a,p)} returns the Legendre symbol \spad{L(a/p)}.
    ++ \spad{L(a/p) = (-1)**((p-1)/2) mod p} (p prime), which is 0 if \spad{a}
    ++ is 0, 1 if \spad{a} is a quadratic residue \spad{mod p} and -1 otherwise.
    ++ Note: because the primality test is expensive,
    ++ if it is known that p is prime then use \spad{jacobi(a,p)}.
  moebiusMu : I -> I
    ++ \spad{moebiusMu(n)} returns the Moebius function \spad{mu(n)}.
    ++ \spad{mu(n)} is either -1,0 or 1 as follows:
    ++ \spad{mu(n) = 0} if n is divisible by a square > 1,
    ++ \spad{mu(n) = (-1)^k} if n is square-free and has k distinct
    ++ prime divisors.
  numberOfDivisors: I -> I
    ++ \spad{numberOfDivisors(n)} returns the number of integers between 1 and n
    ++ (inclusive) which divide n. The number of divisors of n is often
    ++ denoted by \spad{tau(n)}.
  sumOfDivisors : I -> I
    ++ \spad{sumOfDivisors(n)} returns the sum of the integers between 1 and n
    ++ (inclusive) which divide n. The sum of the divisors of n is often
    ++ denoted by \spad{sigma(n)}.
  sumOfKthPowerDivisors: (I,NNI) -> I
    ++ \spad{sumOfKthPowerDivisors(n,k)} returns the sum of the \spad{k}th
    ++ powers of the integers between 1 and n (inclusive) which divide n.
    ++ the sum of the \spad{k}th powers of the divisors of n is often denoted
    ++ by \spad{sigma_k(n)}.
 Implementation ==> add
  import IntegerPrimesPackage(I)

  -- we store the euler and bernoulli numbers computed so far in
  -- a Vector because they are computed from an n-term recurrence
  E: IndexedFlexibleArray(I,0)   := new(1, 1)
  B: IndexedFlexibleArray(RN,0)  := new(1, 1)
  H: Record(Hn:I,Hv:RN) := [1, 1]

  harmonic n ==
    s:I; h:RN
    negative? n => error("harmonic not defined for negative integers")
    if n >= H.Hn then (s,h) := H else (s := 0; h := 0)
    for k in s+1..n repeat h := h + 1/k
    H.Hn := n
    H.Hv := h
    h

  fibonacci n ==
    n = 0 => 0
    negative? n => (odd? n => 1; -1) * fibonacci(-n)
    f1: I := 0
    f2: I := 1
    for k in length(n)-2 .. 0 by -1 repeat
      t := f2**2
      (f1,f2) := (t+f1**2,t+2*f1*f2)
      if bit?(n,k) then (f1,f2) := (f2,f1+f2)
    f2

  euler n ==
    negative? n => error "euler not defined for negative integers"
    odd? n => 0
    l := (#E) :: I
    n < l => E(n)
    concat!(E, new((n+1-l)::NNI, 0)$IndexedFlexibleArray(I,0))
    for i in 1 .. l by 2 repeat E(i) := 0
    -- compute E(i) i = l+2,l+4,...,n given E(j) j = 0,2,...,i-2
    t,e : I
    for i in l+1 .. n by 2 repeat
      t := e := 1
      for j in 2 .. i-2 by 2 repeat
        t := (t*(i-j+1)*(i-j+2)) quo (j*(j-1))
        e := e + t*E(j)
      E(i) := -e
    E(n)

  bernoulli n ==
    negative? n => error "bernoulli not defined for negative integers"
    odd? n =>
      n = 1 => -1/2
      0
    l := (#B) :: I
    n < l => B(n)
    concat!(B, new((n+1-l)::NNI, 0)$IndexedFlexibleArray(RN,0))
    for i in 1 .. l by 2 repeat B(i) := 0
    -- compute B(i) i = l+2,l+4,...,n given B(j) j = 0,2,...,i-2
    for i in l+1 .. n by 2 repeat
      t:I := 1
      b := (1-i)/2
      for j in 2 .. i-2 by 2 repeat
        t := (t*(i-j+2)*(i-j+3)) quo (j*(j-1))
        b := b + (t::RN) * B(j)
      B(i) := -b/((i+1)::RN)
    B(n)

<<inverse : (I,I) -> I>>
<<inverse(a,b)>>
<<chineseRemainder(x1,m1,x2,m2)>>

  jacobi(a,b) ==
    -- Revised by Clifton Williamson January 1989.
    -- Previous version returned incorrect answers when b was even.
    -- The formula J(a/b) = product ( L(a/p) for p in factor b) is only
    -- valid when b is odd (the Legendre symbol L(a/p) is not defined
    -- for p = 2).  When b is even, the Jacobi symbol J(a/b) is only
    -- defined for a = 0 or 1 (mod 4).  When a = 1 (mod 8),
    -- J(a/2) = +1 and when a = 5 (mod 8), we define J(a/2) = -1.
    -- Extending by multiplicativity, we have J(a/b) for even b and
    -- appropriate a.
    -- We also define J(a/1) = 1.
    -- The point of this is the following: if d is the discriminant of
    -- a quadratic field K and chi is the quadratic character for K,
    -- then J(d/n) = chi(n) for n > 0.
    -- Reference: Hecke, Vorlesungen ueber die Theorie der Algebraischen
    -- Zahlen.
    if negative? b then b := -b
    b = 0 => error "second argument of jacobi may not be 0"
    b = 1 => 1
    even? b and positiveRemainder(a,4) > 1 =>
      error "J(a/b) not defined for b even and a = 2 or 3 (mod 4)"
    even? b and even? a => 0
    k : Integer := 0
    while even? b repeat
      b := b quo 2
      k := k + 1
    j:I := (odd? k and positiveRemainder(a,8) = 5 => -1; 1)
    b = 1 => j
    a := positiveRemainder(a,b)
    -- assertion: 0 < a < b and odd? b
    while a > 1 repeat
      if odd? a then
        -- J(a/b) = J(b/a) (-1) ** (a-1)/2 (b-1)/2
        if a rem 4 = 3 and b rem 4 = 3 then j := -j
        (a,b) := (b rem a,a)
      else
        -- J(2*a/b) = J(a/b) (-1) (b**2-1)/8
        k := 0
        until odd? a repeat
          a := a quo 2
          k := k + 1
        if odd? k and (b+2) rem 8 > 4 then j := -j
    a = 0 => 0
    j

  legendre(a,p) ==
    prime? p => jacobi(a,p)
    error "characteristic of legendre must be prime"

  eulerPhi n ==
    n = 0 => 0
    r : RN := 1
    for entry in factors factor n repeat
      r := ((entry.factor - 1) /$RN entry.factor) * r
    numer(n * r)

  divisors n ==
    oldList : List Integer := [1]
    for f in factors factor n repeat
      newList := oldList
      for k in 1..f.exponent repeat
        pow := f.factor ** k
        for m in oldList repeat
          newList := concat(pow * m,newList)
      oldList := newList
    sort(#1 < #2,oldList)

  numberOfDivisors n ==
    n = 0 => 0
    */[1+entry.exponent for entry in factors factor n]

  sumOfDivisors n ==
    n = 0 => 0
    r : RN := */[(entry.factor**(entry.exponent::NNI + 1)-1)/
      (entry.factor-1) for entry in factors factor n]
    numer r

  sumOfKthPowerDivisors(n,k) ==
    n = 0 => 0
    r : RN := */[(entry.factor**(k*entry.exponent::NNI+k)-1)/
      (entry.factor**k-1) for entry in factors factor n]
    numer r

  moebiusMu n ==
    n = 1 => 1
    t := factor n
    for k in factors t repeat
      k.exponent > 1 => return 0
    odd? numberOfFactors t => -1
    1

@
\subsection{TEST INTHEORY}
<<TEST INTHEORY>>=
<<TESTchineseRemainder>>
<<TESTinverse>>
@
\section{package PNTHEORY PolynomialNumberTheoryFunctions}
<<package PNTHEORY PolynomialNumberTheoryFunctions>>=
)abbrev package PNTHEORY PolynomialNumberTheoryFunctions
++ Author: Michael Monagan, Clifton J. Williamson
++ Date Created: June 1987
++ Date Last Updated: 10 November 1996 (Claude Quitte)
++ Basic Operations:
++ Related Domains:
++ Also See:
++ AMS Classifications:
++ Keywords: polynomial, number theory
++ Examples:
++ References: Knuth, The Art of Computer Programming Vol.2
++ Description:
++ This package provides various polynomial number theoretic functions
++ over the integers.
PolynomialNumberTheoryFunctions(): Exports == Implementation where
 I ==> Integer
 RN ==> Fraction I
 SUP ==> SparseUnivariatePolynomial
 NNI ==> NonNegativeInteger

 Exports ==> with
  bernoulli : I -> SUP RN
    ++ bernoulli(n) returns the nth Bernoulli polynomial \spad{B[n](x)}.
    ++ Note: Bernoulli polynomials denoted \spad{B(n,x)} computed by solving the
    ++ differential equation  \spad{differentiate(B(n,x),x) = n B(n-1,x)} where
    ++ \spad{B(0,x) = 1} and initial condition comes from \spad{B(n) = B(n,0)}.
  chebyshevT: I -> SUP I
    ++ chebyshevT(n) returns the nth Chebyshev polynomial \spad{T[n](x)}.
    ++ Note: Chebyshev polynomials of the first kind, denoted \spad{T[n](x)},
    ++ computed from the two term recurrence.  The generating function
    ++ \spad{(1-t*x)/(1-2*t*x+t**2) = sum(T[n](x)*t**n, n=0..infinity)}.
  chebyshevU: I -> SUP I
    ++ chebyshevU(n) returns the nth Chebyshev polynomial \spad{U[n](x)}.
    ++ Note: Chebyshev polynomials of the second kind, denoted \spad{U[n](x)},
    ++ computed from the two term recurrence.  The generating function
    ++ \spad{1/(1-2*t*x+t**2) = sum(T[n](x)*t**n, n=0..infinity)}.
  cyclotomic: I -> SUP I
    ++ cyclotomic(n) returns the nth cyclotomic polynomial \spad{phi[n](x)}.
    ++ Note: \spad{phi[n](x)} is the factor of \spad{x**n - 1} whose roots
    ++ are the primitive nth roots of unity.
  euler     : I -> SUP RN
    ++ euler(n) returns the nth Euler polynomial \spad{E[n](x)}.
    ++ Note: Euler polynomials denoted \spad{E(n,x)} computed by solving the
    ++ differential equation  \spad{differentiate(E(n,x),x) = n E(n-1,x)} where
    ++ \spad{E(0,x) = 1} and initial condition comes from \spad{E(n) = 2**n E(n,1/2)}.
  fixedDivisor: SUP I -> I
    ++ fixedDivisor(a) for \spad{a(x)} in \spad{Z[x]} is the largest integer
    ++ f such that f divides \spad{a(x=k)} for all integers k.
    ++ Note: fixed divisor of \spad{a} is
    ++ \spad{reduce(gcd,[a(x=k) for k in 0..degree(a)])}.
  hermite   : I -> SUP I
    ++ hermite(n) returns the nth Hermite polynomial \spad{H[n](x)}.
    ++ Note: Hermite polynomials, denoted \spad{H[n](x)}, are computed from
    ++ the two term recurrence.  The generating function is:
    ++ \spad{exp(2*t*x-t**2) = sum(H[n](x)*t**n/n!, n=0..infinity)}.
  laguerre  : I -> SUP I
    ++ laguerre(n) returns the nth Laguerre polynomial \spad{L[n](x)}.
    ++ Note: Laguerre polynomials, denoted \spad{L[n](x)}, are computed from
    ++ the two term recurrence.  The generating function is:
    ++ \spad{exp(x*t/(t-1))/(1-t) = sum(L[n](x)*t**n/n!, n=0..infinity)}.
  legendre  : I -> SUP RN
    ++ legendre(n) returns the nth Legendre polynomial \spad{P[n](x)}.
    ++ Note: Legendre polynomials, denoted \spad{P[n](x)}, are computed from
    ++ the two term recurrence.  The generating function is:
    ++ \spad{1/sqrt(1-2*t*x+t**2) = sum(P[n](x)*t**n, n=0..infinity)}.
 Implementation ==> add
  import IntegerPrimesPackage(I)

  x := monomial(1,1)$SUP(I)
  y := monomial(1,1)$SUP(RN)

  -- For functions computed via a fixed term recurrence we record
  -- previous values so that the next value can be computed directly

  E : Record(En:I, Ev:SUP(RN)) := [0,1]
  B : Record( Bn:I, Bv:SUP(RN) ) := [0,1]
  H : Record( Hn:I, H1:SUP(I), H2:SUP(I) ) := [0,1,x]
  L : Record( Ln:I, L1:SUP(I), L2:SUP(I) ) := [0,1,x]
  P : Record( Pn:I, P1:SUP(RN), P2:SUP(RN) ) := [0,1,y]
  CT : Record( Tn:I, T1:SUP(I), T2:SUP(I) ) := [0,1,x]
  U : Record( Un:I, U1:SUP(I), U2:SUP(I) ) := [0,1,0]

  MonicQuotient: (SUP(I),SUP(I)) -> SUP(I)
  MonicQuotient (a,b) ==
    not one? leadingCoefficient(b) => error "divisor must be monic"
    b = 1 => a
    da := degree a
    db := degree b            -- assertion: degree b > 0
    q:SUP(I) := 0
    while da >= db repeat
      t := monomial(leadingCoefficient a, (da-db)::NNI)
      a := a - b * t
      q := q + t
      da := degree a
    q

  cyclotomic n ==
    --++ cyclotomic polynomial denoted phi[n](x)
    p:I; q:I; r:I; s:I; m:NNI; c:SUP(I); t:SUP(I)
    negative? n => error "cyclotomic not defined for negative integers"
    n = 0 => x
    k := n; s := p := 1
    c := x - 1
    while k > 1 repeat
      p := nextPrime p
      (q,r) := divide(k, p)
      if r = 0 then
        while r = 0 repeat (k := q; (q,r) := divide(k,p))
        t := multiplyExponents(c,p::NNI)
        c := MonicQuotient(t,c)
        s := s * p
    m := (n quo s) :: NNI
    multiplyExponents(c,m)

  euler n ==
    p : SUP(RN); t : SUP(RN); c : RN; s : I
    negative? n => error "euler not defined for negative integers"
    if n < E.En then (s,p) := (0$I,1$SUP(RN)) else (s,p) := E
    -- (s,p) := if n < E.En then (0,1) else E
    for i in s+1 .. n repeat
      t := (i::RN) * integrate p
      c := euler(i)$IntegerNumberTheoryFunctions / 2**(i::NNI) - t(1/2)
      p := t + c::SUP(RN)
    E.En := n
    E.Ev := p
    p

  bernoulli n ==
    p : SUP RN; t : SUP RN; c : RN; s : I
    negative? n => error "bernoulli not defined for negative integers"
    if n < B.Bn then (s,p) := (0$I,1$SUP(RN)) else (s,p) := B
    -- (s,p) := if n < B.Bn then (0,1) else B
    for i in s+1 .. n repeat
      t := (i::RN) * integrate p
      c := bernoulli(i)$IntegerNumberTheoryFunctions
      p := t + c::SUP(RN)
    B.Bn := n
    B.Bv := p
    p

  fixedDivisor a ==
    g:I; d:NNI; SUP(I)
    d := degree a
    g := coefficient(a, minimumDegree a)
    for k in 1..d while g > 1 repeat g := gcd(g,a k)
    g

  hermite n ==
    s : I; p : SUP(I); q : SUP(I)
    negative? n => error "hermite not defined for negative integers"
    -- (s,p,q) := if n < H.Hn then (0,1,x) else H
    if n < H.Hn then (s := 0; p := 1; q := x) else (s,p,q) := H
    for k in s+1 .. n repeat (p,q) := (2*x*p-2*(k-1)*q,p)
    H.Hn := n
    H.H1 := p
    H.H2 := q
    p

  legendre n ==
    s:I; t:I; p:SUP(RN); q:SUP(RN)
    negative? n => error "legendre not defined for negative integers"
    -- (s,p,q) := if n < P.Pn then (0,1,y) else P
    if n < P.Pn then (s := 0; p := 1; q := y) else (s,p,q) := P
    for k in s+1 .. n repeat
      t := k-1
      (p,q) := ((k+t)$I/k*y*p - t/k*q,p)
    P.Pn := n
    P.P1 := p
    P.P2 := q
    p

  laguerre n ==
    s:I; t:I; p:SUP(I); q:SUP(I)
    negative? n => error "laguerre not defined for negative integers"
    -- (s,p,q) := if n < L.Ln then (0,1,x) else L
    if n < L.Ln then (s := 0; p := 1; q := x) else (s,p,q) := L
    for k in s+1 .. n repeat
      t := k-1
      (p,q) := ((((k+t)$I)::SUP(I)-x)*p-t**2*q,p)
    L.Ln := n
    L.L1 := p
    L.L2 := q
    p

  chebyshevT n ==
    s : I; p : SUP(I); q : SUP(I)
    negative? n => error "chebyshevT not defined for negative integers"
    -- (s,p,q) := if n < CT.Tn then (0,1,x) else CT
    if n < CT.Tn then (s := 0; p := 1; q := x) else (s,p,q) := CT
    for k in s+1 .. n repeat (p,q) := ((2*x*p - q),p)
    CT.Tn := n
    CT.T1 := p
    CT.T2 := q
    p

  chebyshevU n ==
    s : I; p : SUP(I); q : SUP(I)
    negative? n => error "chebyshevU not defined for negative integers"
    if n < U.Un then (s := 0; p := 1; q := 0) else (s,p,q) := U
    for k in s+1 .. n repeat (p,q) := ((2*x*p - q),p)
    U.Un := n
    U.U1 := p
    U.U2 := q
    p

@
\section{License}
<<license>>=
--Copyright (c) 1991-2002, The Numerical ALgorithms Group Ltd.
--All rights reserved.
--
--Redistribution and use in source and binary forms, with or without
--modification, are permitted provided that the following conditions are
--met:
--
--    - Redistributions of source code must retain the above copyright
--      notice, this list of conditions and the following disclaimer.
--
--    - Redistributions in binary form must reproduce the above copyright
--      notice, this list of conditions and the following disclaimer in
--      the documentation and/or other materials provided with the
--      distribution.
--
--    - Neither the name of The Numerical ALgorithms Group Ltd. nor the
--      names of its contributors may be used to endorse or promote products
--      derived from this software without specific prior written permission.
--
--THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
--IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
--TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
--PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
--OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
--EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
--PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
--PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
--LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
--NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
--SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@
<<*>>=
<<license>>

<<package INTHEORY IntegerNumberTheoryFunctions>>
<<package PNTHEORY PolynomialNumberTheoryFunctions>>
@
\eject
\begin{thebibliography}{99}
\bibitem{1} Cohen, Joel S., 
{\sl Computer Algebra and Symbolic Computation}
{\sl Mathematical Methods},
A.K. Peters, Ltd, Natick, MA. USA (2003)
ISBN 1-56881-159-4
\bibitem{2} Geddes, Keith O., Czapor, Stephen R., Labahn, George
{\sl Algorithms for Computer Algebra}
Kluwer Academic Publishers
ISBN 0-7923-9259-0
\end{thebibliography}
\end{document}