git - alex wennerberg
    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
\ arithmetic on big signed-magnitude numbers
\ requires dmath.fth

\ based on code by leonard francis zettel, jr.
\ https://www.taygeta.com/fsl/library/big.fth
\ released to the forth scientific library.

\ this is a forth implementation of the "classical algorithms".
\ see knuth, donald the art of computer programming vol 2 p 250.

\ the internal representation of the big numbers is "little-endian
\ signed magnitude".  the cell at the address addr contains n, the
\ size of the number in digits.  n is positive for positive numbers,
\ negative for negative.  each succeeding cell contains a digit of
\ the number in base 2**(cell size-1), least significant digit first.

base @ decimal \ housekeeping

\ helper words
: leave unloop exit ;

32767 constant biggest            \ largest representable signed number
biggest 1+ constant bigbase       \ big number base as an unsigned number
biggest s>d 1 m+ 2constant bigbd  \ big number base as a double number

: >body ( xt -- a ) ;  \ Do nothing?
: cell- ( a1 -- a2 ) 1 cells - ;
: digit ( c n1 -- n2 f ) \ returns valid
    over [char] 0 <
    if   2drop false   \ characters below the zero character can't be digits
    else over [char] : <
         if   drop [char] 0 - true
         else over [char] a <
              if   2drop false
              else swap [char] 7 -        \ convert to numeric value
                   dup rot <
                   if   true              \ valid digit
                   else drop false
                   then
              then
         then
    then ;

: carry ( digit -- carry digit)  
  \ check for a carry, remove it,  it under the result.
  biggest over u< if bigbase - else 0 then swap ;

: d>carry ( low high -- carry digit) \ convert a double number to a
  \ low-order digit and a carry.
  bigbase um/mod swap ;

: overflow? ( borrow uj -- uj new_borrow) \ if uj is negative (indicating
  \ a result out of range on the previous subtraction), bring it in
  \ range and increment the borrow that will be necessary on the next
  \ digit.
  dup 0< if  bigbase +  1  else 0  then rot + ;

: big_digit_pointer ( -- ) ( n -- a )  \ create a word <name>.
  \ when <name> is executed return the address of the nth cell after
  \ the address in <name>'s data field.
  create 1 cells allot           \ create the word & allot the data space
  does> @                        \ put the address in <name>'s data field
                                 \ on the stack
        swap cells + ;           \ increment the address by index cells


: to_pointer ( <name> -- a1 ) \ compiling: a1 is <name>'s data field.
             ( a2 --) \ execution: a2 is placed in <name>'s
                         \ data field.
   ' >body postpone literal  postpone ! ; immediate
.s cr

\ TODO figure out what the heck is happening here and fix it 
\ I think the issue is the word represntation is different here
big_digit_pointer clippee
.s
\ broken 
: clip ( addr --)      \ remove leading zeroes from the number at addr
  to_pointer clippee
  0                    \ default - no non-zero digits
  1  0 clippee @ abs   \ loop from present number of digits to one.
  do       i clippee @               \ next big digit
           0<> if drop i leave then  \ index of first non-zero digit
                                     \ on stack
  -1 +loop
  ?dup if   0 clippee @              \ original sign & size
            0< if negate then        \ minus sign on new size
       else 1                        \ number is exactly zero, keep one
                                     \ of the zeros, show plus number
       then
  0 clippee ! ;                 \ store new size
\ something added to stack

: big_digit ( addr n1 -- n2k) \ return digit n1 of the big number at addr.
  \ if n1 is greater than the number of digits, return a leading zero.
  over @ abs         \ number of digits
  over <
  if   2drop 0       \ return leading zero
  else cells + @     \ return digit
  then ;

: bignegate ( addr --) \ change the sign of the big number at addr
  dup @ dup                          \ number of digits
  1 = if   over cell+ @              \ check for zero
           if   negate swap !        \ non-zero, negate
           else 2drop                \ zero, do nothing
           then
      else negate swap !
      then ;

: bigabs ( addr --) \ give the big number at addr its absolute value,
  dup @ abs swap ! ;

: big>here ( addr --)    \ "big to here" append the big number at addr
                         \ to the end of data space.
  here                   \ address to move to
  over @ abs 1+ cells    \ number of address units in the number
  dup allot              \ allot space for the number
  cmove ;


: adjust_sign ( a1 a2 a3 -- a3)  \ adjust the sign of the big
  \ number at a3 according to the rules for forming the algebraic
  \ product from the operands at a1 and a2
  rot @  rot @  xor 0< if dup bignegate then ;

\ move the number at a1 to a2 and free any data space beyond it.
: reposition ( a1 a2 -- )
  swap 2dup @                    \ (a2 a1 a2 size)
  abs 1+ cells                   \ (a2 a1 a2 bytes)
  dup >r  cmove                   \ (a2) (bytes)
  r> + here - allot ;

big_digit_pointer |big|1  big_digit_pointer |big|2

: |big|= ( a1 a2 -- flag) \ true if the big number at a1 has the
  \ same absolute value as the big number at a2.  false otherwise
  over @ abs over @ abs =     \  are the numbers the same size?
  if   to_pointer |big|1
       to_pointer |big|2
       true                   \ default initial flag.
       1  0 |big|1 @  abs
       do i |big|1 @
          i |big|2 @  <>
          if drop false leave then
          -1
       +loop
  else 2drop false
  then ;

: |big|< ( a1 a2 -- flag) \ true if the absolute value of the
  \ big number at a1 is less than the absolute value of the big number
  \ at a2.  false otherwise.

  to_pointer |big|2
  to_pointer |big|1
  0 |big|1 @ abs
  0 |big|2 @ abs
  2dup <
  if   2drop true
  else = false                   \ default flag if equal, result if <>.
       swap
       if   1 0 |big|1 @ abs     \ from the high order digit to the first
            do                   \ digit
               i |big|1 @
               i |big|2 @
               2dup
               <> if < nip leave  then
               2drop
            -1 +loop
       then
  then ;


: big0= ( addr -- flag) \ return true if the big number at addr is zero.
  dup @ 1 = if cell+ @ 0= else drop false then ;

: big0<> ( addr -- flag) \ return true if the big number at addr is not zero.
  big0= 0= ;

: big0< ( addr -- flag) @ 0< ;

: big< ( a1 a2 -- flag) \ true if the operand at a1 is less than
                            \ the operand at a2.  false otherwise.
  over @ over @ <           \ look at operand sign & number of digits
  if   2drop true
  else                      \ ( a1 a2)
       over @ over @ >
       if   2drop false
       else                 \ to get here the operands must be the
                            \ same sign & be of equal length
            dup @ 0<
            if swap then    \ if the numbers are negative, the one with
                            \ the larger absolute value is the lesser.
            dup @ abs >r    \ park number of digits
            r@ cells
            dup rot +       \ high order cell of operand 2.
            rot rot +       \ high order cell of operand 1.
            swap
            false           \ dummy initial flag
            r> 0
            do                      \ ( a1 a2 flag)
               drop                 \ flag from previous cycle
               over @ over @        \ ( a1 a2 digit1 digit2)
               < dup if leave then
               rot cell- rot cell-
               rot
            loop
            nip nip
       then
 then
 ;
: big= ( a1 a2 -- flag) \ true if the big number at a1 has the
  \ same absolute value as the big number at a2.  false otherwise
  over @  over @  =     \  are the numbers the same size?
  if   to_pointer |big|1
       to_pointer |big|2
       true                   \ default initial flag.
       1  0 |big|1 @  abs
       do i |big|1 @
          i |big|2 @  <>
          if drop false leave then
          -1
       +loop
  else 2drop false
  then ;


\ words doing mixed single-precision and big number arithmetic

big_digit_pointer big_addend

: big+s ( addr n --) \ add n to the number at addr.  n must be non-negative
  \ the number at addr must be non-negative and end at here.
  swap to_pointer big_addend  \ ( n)
  0 big_addend @  abs 1+      \ loop limit
  1                           \ loop start
  do                          \ ( n)
       i big_addend @  +      \ ( ui+n)
       carry
       i big_addend !         \ store new ui
       dup 0= if leave then   \ no carry, we are done
  loop
                              \ carry in high-order digit?
  if
      1 ,                     \ append carry to the number
      1  0 big_addend +!      \ increment number size
  then ;

big_digit_pointer big_multiplicand
: big*s ( addr n -- )         \ multiply the number at addr by n.
                              \ n must be positive
                              \ the number at addr must end at "here"
  swap
  to_pointer big_multiplicand
  0                           \ ( n carry)
  0 big_multiplicand @ abs 1+
  1
  do                          \ ( n carry)
       over
       i big_multiplicand @
       m*                     \ ( carry n low[ui*n] high[ui*n])
       rot  m+                \ ( n low[ui*n+carry] high[ui*n+carry])
       d>carry                \ ( n carry ui*n)
       i big_multiplicand !   \ store digit i back in u ( n carry)
  loop
  nip
  ?dup if  0 big_multiplicand  @  \ ( carry n)
            dup 0< if   1-
                   else 1+
                   then           \ ( carry n)
            0 big_multiplicand  ! ,
       then ;


big_digit_pointer big_dividend

: big/mods ( addr n1 -- n2) \ "big slash-mod s".  divide the big number at
  \ addr by n1, leaving the quotient at addr.  n2 is the remainder.
  swap
  to_pointer big_dividend
  0                         \ ( divisor remainder)
  1  0 big_dividend @ abs
  do                        \ ( divisor remainder)
      bigbase um*           \ ( divisor lowr highr)
      i big_dividend @      \ ( divisor divisor lowr highr uj)
      m+
      2 pick
      um/mod                \ ( divisor r wj )
      i big_dividend !
      -1
  +loop
  nip  0 big_dividend clip ;


\ words for going from characters to big numbers

: >big_number ( a1 a2 u1 -- a1 a3 u2) \ "to big number"
  \ extend the big number at a1 by the number represented by the
  \ string of u1 characters at a2.  a3 is the address of the first
  \ unconverted character and u2 is the number of unconverted characters
  2dup + >r                    \ address just beyond end of string on
                               \ return stack
  0 do                         \ ( a1 a2)
        2dup c@                \ ( a1 a2 a1 char)
        base @ digit           \ ( a1 a2 a1 n flag)
        if   over base @ big*s \ ( a1 a2 a1 n)
             big+s             \ ( a1 a2)
        else                   \ ( a1 a2 a1 char)
             drop leave \ ( a1 a2)
        then
        1+
    loop
    r> over - ;


: make_big_number ( a1 u -- a2)  \ convert the u characters at a1
                                       \ to a big number at a2
  \ if the first character is "-" (ascii 45) the result will be negative.
  \ embedded commas are ignored 
  \ (usa representation convention for large numbers)
  \ conversion stops at the first non-convertible character.
             
  over c@                          \ get the first character
  [char] - =                       \ is it a minus sign?
  dup >r
  if   swap 1+ swap 1- then     \ adjust to next character
                                   \ ( a1 u)
  here 1 , 0 ,                     \ create big number = 0
                                   \ ( a1 u a2)
  rot rot
  begin                            \ ( a2 a1 u)
        >big_number                \ ( a2 a1 u)
        over c@ [char] , =         \ ( a2 a1 u flag)
        over and
  while
        swap 1+ swap 1-
  repeat
  2drop
  r> if dup bignegate then ;


\ words for big number output
\ the words <big# through big#s and big. are adapted from
\ descriptions of their pictured numeric output string counterparts
\ in "all about forth" 2nd ed by glen haydon.  used with permission.

create big_string 256 allot

variable bighld

: <big# ( --) \ "less big number sign"
              \ initialize the big number pictured numeric output area
  big_string 256 + bighld ! ;  \ haydon p 67

: bighold ( c -- ) \ add c to the beginning of the big pictured numeric
                   \ output string
  -1 bighld +!  bighld @ c! ; \ haydon p 170.

: #big> ( a1 -- a2 +n) \ "number sign big less".  end big number
                             \ pictured output conversion
  drop  bighld @             \ start of string
  big_string 256 +     \ one past end of string
  over - 1 / ;         \ length of string

: big# ( addr -- addr)  \ "big number sign"
  \ generate the next ascii character from the big number at addr.
  \ afterward the big number at addr will hold the quotient obtained
  \ by dividing its previous value by the value in base.
  \ this result can then be used for further processing.
  \ haydon p 18

  dup
  base @ big/mods       \ next digit
  9 over <              \ is it bigger than a decimal digit?
  if 7 + then           \ add seven to its character representation,
                        \ thus skipping the ascii codes between 9 and a.
  48 +                  \ convert from number to ascii character code.
  bighold ;             \ add the character to the front of the output
                        \ string

: big#s ( addr -- addr) \ "big number sign s"  convert all digits of the
                        \ big number at addr to big numeric output, leaving
                        \ zero at addr
  begin big# dup @ 1 =      \ down to length 1
        over cell+ @ 0=     \ remaining cell is zero
        and
  until ;               \ haydon p 21.

: bigsign ( n --) \ put a minus sign in the big pictured numeric
                   \ character output string if n is negative
  0< if 45 bighold then ; \ haydon p 222.

: bigstring ( a1 sign --  ) \ display the big number at a1 with
  \ the sign of the number in sign.

  <big# big#s swap bigsign #big> type ;

\ words doing arithmetic on two big numbers

big_digit_pointer long_addend  big_digit_pointer short_addend

: sum ( a1 a2 - a3) \ a3 has the result of adding the absolute
  \ value of the big number at a1 to the absolute value of the big
  \ number at a2.

  over @ abs  over @ abs <     \ compare the size of the addends
  if swap then
  to_pointer short_addend
  to_pointer long_addend

  here                         \ address of result
  0 ,                          \ dummy placeholder for the count of the
                               \ result
  0                            \ initialize carry

  0 short_addend @ abs 1+      \ for each digit in the short addend
  1                            \ starting at the first
  do
       i short_addend @  +     \ add digit to carry
       i long_addend  @  +     \ add digit to previous sum
       carry ,                 \ new carry, append digit to result
  loop

  0 long_addend @ abs          \ number of digits in long operand
  1+                           \ jog to make do end on last digit
  0 short_addend @ abs         \ number of digits in short operand
  1+                           \ jog to start do on first digit
                               \ not yet used
  ?do  i long_addend @  +      \ append any remaining digits to the
       carry ,                 \ result, rippling the carry as
  loop                         \ necessary

  0 long_addend @ abs          \ result size so far
  swap
  if 1 , 1+ then               \ if final carry, append to result,
                               \ bump size
  over ! ;                     \ store result size.

big_digit_pointer minuend  big_digit_pointer subtrahend

: difference ( a1 a2 -- a3 )
  \ a3 is the address of the difference of the absolute values of
  \ the big number at a1 and the big number at a2.

  here >r                       \ park address of result
  2dup |big|=
  if   2drop  1 , 0 ,           \ equal absolute values, result is zero
  else
       2dup |big|<
       if swap then
       to_pointer subtrahend
       to_pointer minuend
       0 minuend @ abs  ,       \ count of the result
       0                        \ initialize borrow

       0 minuend @ abs 1+       \ for each minuend digit
       1                        \ starting with the first
       do                       \ ( borrow)
          0                     \ next borrow
          i minuend @           \ get the ith minuend digit
          rot -                 \ subtract previous borrow
          overflow?
          swap                  \ ( borrow result)
          0 subtrahend
          i big_digit  -        \ subtract the ith subtrahend digit
          overflow?             \ ( result borrow)
          swap ,                \ append result
       loop

       drop                 \ get rid of final borrow (it will be zero)
       r@ clip              \ remove leading zeroes
  then
  r> ;                      \ address of result on stack