1 ! Copyright (C) 2009 Joe Groff, 2013 John Benediktsson
2 ! See http://factorcode.org/license.txt for BSD license.
3 USING: accessors byte-arrays combinators kernel kernel.private
4 layouts make math math.private namespaces sbufs sequences
5 sequences.private splitting strings strings.private ;
10 { [ dup CHAR: 9 <= ] [ CHAR: 0 - dup 0 < [ drop 255 ] when ] }
11 { [ dup CHAR: a < ] [ CHAR: A 10 - - dup 10 < [ drop 255 ] when ] }
12 [ CHAR: a 10 - - dup 10 < [ drop 255 ] when ]
15 ERROR: invalid-radix radix ;
21 { length fixnum read-only }
22 { radix fixnum read-only } ;
24 : <number-parse> ( str radix -- i number-parse n )
25 [ 0 ] 2dip [ dup length ] dip number-parse boa 0 ; inline
27 : (next-digit) ( i number-parse n digit-quot end-quot -- n/f )
28 [ 2over length>> < ] 2dip
29 [ [ 2over str>> nth-unsafe >fixnum [ 1 fixnum+fast ] 3dip ] prepose ] dip if ; inline
31 : require-next-digit ( i number-parse n quot -- n/f )
32 [ 3drop f ] (next-digit) ; inline
34 : next-digit ( i number-parse n quot -- n/f )
35 [ 2nip ] (next-digit) ; inline
37 : add-digit ( i number-parse n digit quot -- n/f )
38 [ [ dup radix>> ] [ * ] [ + ] tri* ] dip next-digit ; inline
40 : digit-in-radix ( number-parse n char -- number-parse n digit ? )
41 digit> pick radix>> over > ; inline
43 : ?make-ratio ( num denom/f -- ratio/f )
44 [ / ] [ drop f ] if* ; inline
47 { radix fixnum read-only }
49 { exponent read-only } ;
51 : inc-point ( float-parse -- float-parse' )
52 [ radix>> ] [ point>> 1 + ] [ exponent>> ] tri float-parse boa ; inline
54 : store-exponent ( float-parse n expt -- float-parse' n )
55 swap [ [ radix>> ] [ point>> ] bi ] 2dip [ float-parse boa ] dip ; inline
57 : ?store-exponent ( float-parse n expt/f -- float-parse' n/f )
58 [ store-exponent ] [ drop f ] if* ; inline
60 : ((pow)) ( base x -- base^x )
63 dup odd? [ [ [ * ] keep ] [ 1 - ] bi* ] when
65 ] until 2drop ; inline
67 : (pow) ( base x -- base^x )
69 dup 0 >= [ ((pow)) ] [ [ recip ] [ neg ] bi* ((pow)) ] if ; inline
71 : add-mantissa-digit ( float-parse i number-parse n digit quot -- float-parse' n/f )
72 [ [ inc-point ] 4dip ] dip add-digit ; inline
74 : make-float-dec-exponent ( float-parse n/f -- float/f )
75 [ [ radix>> ] [ point>> ] [ exponent>> ] tri - (pow) ] [ swap /f ] bi* ; inline
77 : make-float-bin-exponent ( float-parse n/f -- float/f )
78 [ drop [ radix>> ] [ point>> ] bi (pow) ]
80 [ drop 2.0 swap exponent>> (pow) * ] 2tri ; inline
82 : ?default-exponent ( float-parse n/f -- float-parse' n/f' )
84 over radix>> 10 = [ 0 store-exponent ] [ drop f ] if
87 : ?make-float ( float-parse n/f -- float/f )
88 { float-parse object } declare
91 { [ dup not ] [ 2drop f ] }
92 { [ over radix>> 10 = ] [ make-float-dec-exponent ] }
93 [ make-float-bin-exponent ]
96 : ?neg ( n/f -- -n/f )
97 [ neg ] [ f ] if* ; inline
99 : ?add-ratio ( m n/f -- m+n/f )
100 dup ratio? [ + ] [ 2drop f ] if ; inline
102 : @abort ( i number-parse n x -- f )
105 : @split ( i number-parse n -- n i number-parse n' )
108 : @split-exponent ( i number-parse n -- n i number-parse' n' )
109 -rot [ str>> ] [ length>> ] bi 10 number-parse boa 0 ; inline
111 : <float-parse> ( i number-parse n -- float-parse i number-parse n )
112 [ drop nip radix>> 0 f float-parse boa ] 3keep ; inline
114 DEFER: @exponent-digit
115 DEFER: @mantissa-digit
121 : @exponent-digit-or-punc ( float-parse i number-parse n char -- float-parse n/f )
123 { CHAR: , [ [ @exponent-digit ] require-next-digit ] }
127 : @exponent-digit ( float-parse i number-parse n char -- float-parse n/f )
128 { float-parse fixnum number-parse integer fixnum } declare
129 digit-in-radix [ [ @exponent-digit-or-punc ] add-digit ] [ @abort ] if ;
131 : @exponent-first-char ( float-parse i number-parse n char -- float-parse n/f )
133 { CHAR: - [ [ @exponent-digit ] require-next-digit ?neg ] }
134 { CHAR: + [ [ @exponent-digit ] require-next-digit ] }
138 : ->exponent ( float-parse i number-parse n -- float-parse' n/f )
139 @split-exponent [ @exponent-first-char ] require-next-digit ?store-exponent ; inline
141 : exponent-char? ( number-parse n char -- number-parse n char ? )
143 { 10 [ dup CHAR: e = [ t ] [ dup CHAR: E = ] if ] }
144 [ drop dup CHAR: p = [ t ] [ dup CHAR: P = ] if ]
147 : or-exponent ( i number-parse n char quot -- n/f )
148 [ exponent-char? [ drop <float-parse> ->exponent ?make-float ] ] dip if ; inline
150 : or-mantissa->exponent ( float-parse i number-parse n char quot -- float-parse n/f )
151 [ exponent-char? [ drop ->exponent ] ] dip if ; inline
153 : @mantissa-digit-or-punc ( float-parse i number-parse n char -- float-parse n/f )
155 { CHAR: , [ [ @mantissa-digit ] require-next-digit ] }
159 : @mantissa-digit ( float-parse i number-parse n char -- float-parse n/f )
160 { float-parse fixnum number-parse integer fixnum } declare
163 [ [ @mantissa-digit-or-punc ] add-mantissa-digit ]
165 ] or-mantissa->exponent ;
167 : ->mantissa ( i number-parse n -- n/f )
168 <float-parse> [ @mantissa-digit ] next-digit ?make-float ; inline
170 : ->required-mantissa ( i number-parse n -- n/f )
171 <float-parse> [ @mantissa-digit ] require-next-digit ?make-float ; inline
173 : @denom-digit-or-punc ( i number-parse n char -- n/f )
175 { CHAR: , [ [ @denom-digit ] require-next-digit ] }
176 { CHAR: . [ ->mantissa ] }
177 [ [ @denom-digit ] or-exponent ]
180 : @denom-digit ( i number-parse n char -- n/f )
181 { fixnum number-parse integer fixnum } declare
182 digit-in-radix [ [ @denom-digit-or-punc ] add-digit ] [ @abort ] if ;
184 : @denom-first-digit ( i number-parse n char -- n/f )
186 { CHAR: . [ ->mantissa ] }
190 : ->denominator ( i number-parse n -- n/f )
191 { fixnum number-parse integer } declare
192 @split [ @denom-first-digit ] require-next-digit ?make-ratio ;
194 : @num-digit-or-punc ( i number-parse n char -- n/f )
196 { CHAR: , [ [ @num-digit ] require-next-digit ] }
197 { CHAR: / [ ->denominator ] }
201 : @num-digit ( i number-parse n char -- n/f )
202 { fixnum number-parse integer fixnum } declare
203 digit-in-radix [ [ @num-digit-or-punc ] add-digit ] [ @abort ] if ;
205 : ->numerator ( i number-parse n -- n/f )
206 { fixnum number-parse integer } declare
207 @split [ @num-digit ] require-next-digit ?add-ratio ;
209 : @pos-digit-or-punc ( i number-parse n char -- n/f )
211 { CHAR: , [ [ @pos-digit ] require-next-digit ] }
212 { CHAR: + [ ->numerator ] }
213 { CHAR: / [ ->denominator ] }
214 { CHAR: . [ ->mantissa ] }
215 [ [ @pos-digit ] or-exponent ]
218 : @pos-digit ( i number-parse n char -- n/f )
219 { fixnum number-parse integer fixnum } declare
220 digit-in-radix [ [ @pos-digit-or-punc ] add-digit ] [ @abort ] if ;
222 : (->radix) ( number-parse radix -- number-parse' )
223 [ [ str>> ] [ length>> ] bi ] dip number-parse boa ; inline
225 : ->radix ( i number-parse n quot radix -- i number-parse n quot )
226 [ (->radix) ] curry 2dip ; inline
228 : with-radix-char ( i number-parse n radix-quot nonradix-quot -- n/f )
231 { CHAR: b [ drop 2 ->radix require-next-digit ] }
232 { CHAR: o [ drop 8 ->radix require-next-digit ] }
233 { CHAR: x [ drop 16 ->radix require-next-digit ] }
234 { f [ 3drop 2drop 0 ] }
235 [ [ drop ] 2dip swap call ]
237 ] 2curry next-digit ; inline
239 : @pos-first-digit ( i number-parse n char -- n/f )
241 { CHAR: . [ ->required-mantissa ] }
242 { CHAR: 0 [ [ @pos-digit ] [ @pos-digit-or-punc ] with-radix-char ] }
246 : @neg-digit-or-punc ( i number-parse n char -- n/f )
248 { CHAR: , [ [ @neg-digit ] require-next-digit ] }
249 { CHAR: - [ ->numerator ] }
250 { CHAR: / [ ->denominator ] }
251 { CHAR: . [ ->mantissa ] }
252 [ [ @neg-digit ] or-exponent ]
255 : @neg-digit ( i number-parse n char -- n/f )
256 { fixnum number-parse integer fixnum } declare
257 digit-in-radix [ [ @neg-digit-or-punc ] add-digit ] [ @abort ] if ;
259 : @neg-first-digit ( i number-parse n char -- n/f )
261 { CHAR: . [ ->required-mantissa ] }
262 { CHAR: 0 [ [ @neg-digit ] [ @neg-digit-or-punc ] with-radix-char ] }
266 : @first-char ( i number-parse n char -- n/f )
268 { CHAR: - [ [ @neg-first-digit ] require-next-digit ?neg ] }
269 { CHAR: + [ [ @pos-first-digit ] require-next-digit ] }
273 : @first-char-no-radix ( i number-parse n char -- n/f )
275 { CHAR: - [ [ @neg-digit ] require-next-digit ?neg ] }
276 { CHAR: + [ [ @pos-digit ] require-next-digit ] }
282 : string>number ( str -- n/f )
283 10 <number-parse> [ @first-char ] require-next-digit ;
285 : base> ( str radix -- n/f )
286 <number-parse> [ @first-char-no-radix ] require-next-digit ;
288 : bin> ( str -- n/f ) 2 base> ; inline
289 : oct> ( str -- n/f ) 8 base> ; inline
290 : dec> ( str -- n/f ) 10 base> ; inline
291 : hex> ( str -- n/f ) 16 base> ; inline
293 : string>digits ( str -- digits )
294 [ digit> ] B{ } map-as ; inline
298 : (digits>integer) ( valid? accum digit radix -- valid? accum )
299 2dup < [ swapd * + ] [ 4drop f 0 ] if ; inline
301 : each-digit ( seq radix quot -- n/f )
302 [ t 0 ] 3dip curry each swap [ drop f ] unless ; inline
306 : digits>integer ( seq radix -- n/f )
307 [ (digits>integer) ] each-digit ; inline
310 dup 10 < [ CHAR: 0 + ] [ 10 - CHAR: a + ] if ; inline
315 48 48 48 48 48 48 48 48 48 48 49 49 49 49 49 49 49 49 49 49
316 50 50 50 50 50 50 50 50 50 50 51 51 51 51 51 51 51 51 51 51
317 52 52 52 52 52 52 52 52 52 52 53 53 53 53 53 53 53 53 53 53
318 54 54 54 54 54 54 54 54 54 54 55 55 55 55 55 55 55 55 55 55
319 56 56 56 56 56 56 56 56 56 56 57 57 57 57 57 57 57 57 57 57
323 48 49 50 51 52 53 54 55 56 57 48 49 50 51 52 53 54 55 56 57
324 48 49 50 51 52 53 54 55 56 57 48 49 50 51 52 53 54 55 56 57
325 48 49 50 51 52 53 54 55 56 57 48 49 50 51 52 53 54 55 56 57
326 48 49 50 51 52 53 54 55 56 57 48 49 50 51 52 53 54 55 56 57
327 48 49 50 51 52 53 54 55 56 57 48 49 50 51 52 53 54 55 56 57
330 : (two-digit) ( num accum -- num' accum )
332 100 /mod [ TENS nth-unsafe ] [ ONES nth-unsafe ] bi
333 ] dip [ push ] keep [ push ] keep ; inline
335 : (one-digit) ( num accum -- num' accum )
336 [ 10 /mod CHAR: 0 + ] dip [ push ] keep ; inline
338 : (bignum>dec) ( num accum -- num' accum )
339 [ over most-positive-fixnum > ]
340 [ { bignum sbuf } declare (two-digit) ] while
341 [ >fixnum ] dip ; inline
343 : (fixnum>dec) ( num accum -- num' accum )
344 { fixnum sbuf } declare
345 [ over 10 >= ] [ (two-digit) ] while
346 [ over zero? ] [ (one-digit) ] until ; inline
348 GENERIC: (positive>dec) ( num -- str )
350 M: bignum (positive>dec)
351 12 <sbuf> (bignum>dec) (fixnum>dec) "" like reverse! nip ; inline
353 : (count-digits) ( digits n -- digits' )
355 { [ dup 10 < ] [ drop ] }
356 { [ dup 100 < ] [ drop 1 fixnum+fast ] }
357 { [ dup 1,000 < ] [ drop 2 fixnum+fast ] }
359 dup 1,000,000,000,000 < [
371 dup 10,000,000,000 < [
372 1,000,000,000 >= 9 8 ?
374 100,000,000,000 >= 11 10 ?
378 [ 12 fixnum+fast ] [ 1,000,000,000,000 /i ] bi*
382 } cond ; inline recursive
384 M: fixnum (positive>dec)
385 1 over (count-digits) <sbuf> (fixnum>dec) "" like reverse! nip ; inline
387 : (positive>base) ( num radix -- str )
388 dup 1 <= [ invalid-radix ] when
389 [ dup 0 > ] swap [ /mod >digit ] curry "" produce-as nip
392 : positive>base ( num radix -- str )
393 dup 10 = [ drop (positive>dec) ] [ (positive>base) ] if ; inline
397 GENERIC# >base 1 ( n radix -- str )
399 : number>string ( n -- str ) 10 >base ; inline
401 : >bin ( n -- str ) 2 >base ; inline
402 : >oct ( n -- str ) 8 >base ; inline
403 : >hex ( n -- str ) 16 >base ; inline
412 [ neg ] dip positive>base CHAR: - prefix
417 [ [ 0 < ] [ abs 1 /mod ] bi ]
418 [ [ positive>base ] curry ] bi*
420 [ [ numerator ] [ denominator ] bi ] dip bi@ "/" glue
421 ] keep rot [ drop ] [
422 swap call pick "-" "+" ? rot 3append
423 ] if-zero swap [ CHAR: - prefix ] when ;
427 : fix-float ( str -- newstr )
428 CHAR: e over member? [
429 "e" split1 [ fix-float ] dip "e" glue
431 CHAR: . over member? [ ".0" append ] unless
434 : mantissa-expt-normalize ( mantissa expt -- mantissa' expt' )
435 [ dup log2 52 swap - [ shift 52 2^ 1 - bitand ] [ 1022 + neg ] bi ]
438 : mantissa-expt ( float -- mantissa expt )
440 [ -0.0 double>bits bitnot bitand -52 shift ] bi
441 mantissa-expt-normalize ;
443 : float>hex-sign ( bits -- str )
444 -0.0 double>bits bitand zero? "" "-" ? ;
446 : float>hex-value ( mantissa -- str )
447 >hex 13 CHAR: 0 pad-head [ CHAR: 0 = ] trim-tail
448 [ "0" ] when-empty "1." prepend ;
450 : float>hex-expt ( mantissa -- str )
451 10 >base "p" prepend ;
453 : float>hex ( n -- str )
456 mantissa-expt [ float>hex-value ] [ float>hex-expt ] bi*
459 : format-string ( format -- format )
460 0 suffix >byte-array ; foldable
462 : format-head ( byte-array n -- string )
463 swap over 0 <string> [
465 [ [ nth-unsafe ] 2keep drop ]
466 [ set-string-nth-fast ] bi*
467 ] 2curry each-integer
470 : format-float ( n format -- string )
471 format-string (format-float)
472 dup [ 0 = ] find drop
473 format-head fix-float ; inline
475 : float>base ( n radix -- str )
478 { 10 [ "%.16g" format-float ] }
486 { [ over fp-nan? ] [ 2drop "0/0." ] }
487 { [ over 1/0. = ] [ 2drop "1/0." ] }
488 { [ over -1/0. = ] [ 2drop "-1/0." ] }
489 { [ over 0.0 fp-bitwise= ] [ 2drop "0.0" ] }
490 { [ over -0.0 fp-bitwise= ] [ 2drop "-0.0" ] }
494 : # ( n -- ) number>string % ; inline