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 ;
9 PRIMITIVE: (format-float) ( n format -- byte-array )
14 { [ dup CHAR: 9 <= ] [ CHAR: 0 - dup 0 < [ drop 255 ] when ] }
15 { [ dup CHAR: a < ] [ CHAR: A 10 - - dup 10 < [ drop 255 ] when ] }
16 [ CHAR: a 10 - - dup 10 < [ drop 255 ] when ]
19 ERROR: invalid-radix radix ;
25 { length fixnum read-only }
26 { radix fixnum read-only } ;
28 : <number-parse> ( str radix -- i number-parse n )
29 [ 0 ] 2dip [ dup length ] dip number-parse boa 0 ; inline
31 : (next-digit) ( i number-parse n digit-quot end-quot -- n/f )
32 [ 2over length>> < ] 2dip
33 [ [ 2over str>> nth-unsafe >fixnum [ 1 fixnum+fast ] 3dip ] prepose ] dip if ; inline
35 : require-next-digit ( i number-parse n quot -- n/f )
36 [ 3drop f ] (next-digit) ; inline
38 : next-digit ( i number-parse n quot -- n/f )
39 [ 2nip ] (next-digit) ; inline
41 : add-digit ( i number-parse n digit quot -- n/f )
42 [ [ dup radix>> ] [ * ] [ + ] tri* ] dip next-digit ; inline
44 : digit-in-radix ( number-parse n char -- number-parse n digit ? )
45 digit> pick radix>> over > ; inline
47 : ?make-ratio ( num denom/f -- ratio/f )
48 [ / ] [ drop f ] if* ; inline
51 { radix fixnum read-only }
53 { exponent read-only } ;
55 : inc-point ( float-parse -- float-parse' )
56 [ radix>> ] [ point>> 1 + ] [ exponent>> ] tri float-parse boa ; inline
58 : store-exponent ( float-parse n expt -- float-parse' n )
59 swap [ [ radix>> ] [ point>> ] bi ] 2dip [ float-parse boa ] dip ; inline
61 : ?store-exponent ( float-parse n expt/f -- float-parse' n/f )
62 [ store-exponent ] [ drop f ] if* ; inline
64 : ((pow)) ( base x -- base^x )
67 dup odd? [ [ [ * ] keep ] [ 1 - ] bi* ] when
69 ] until 2drop ; inline
71 : (pow) ( base x -- base^x )
73 dup 0 >= [ ((pow)) ] [ [ recip ] [ neg ] bi* ((pow)) ] if ; inline
75 : add-mantissa-digit ( float-parse i number-parse n digit quot -- float-parse' n/f )
76 [ [ inc-point ] 4dip ] dip add-digit ; inline
78 : make-float-dec-exponent ( float-parse n/f -- float/f )
79 [ [ radix>> ] [ point>> ] [ exponent>> ] tri - (pow) ] [ swap /f ] bi* ; inline
81 : make-float-bin-exponent ( float-parse n/f -- float/f )
82 [ drop [ radix>> ] [ point>> ] bi (pow) ]
84 [ drop 2.0 swap exponent>> (pow) * ] 2tri ; inline
86 : ?default-exponent ( float-parse n/f -- float-parse' n/f' )
88 over radix>> 10 = [ 0 store-exponent ] [ drop f ] if
91 : ?make-float ( float-parse n/f -- float/f )
92 { float-parse object } declare
95 { [ dup not ] [ 2drop f ] }
96 { [ over radix>> 10 = ] [ make-float-dec-exponent ] }
97 [ make-float-bin-exponent ]
100 : ?neg ( n/f -- -n/f )
101 [ neg ] [ f ] if* ; inline
103 : ?add-ratio ( m n/f -- m+n/f )
104 dup ratio? [ + ] [ 2drop f ] if ; inline
106 : @abort ( i number-parse n x -- f )
109 : @split ( i number-parse n -- n i number-parse n' )
112 : @split-exponent ( i number-parse n -- n i number-parse' n' )
113 -rot [ str>> ] [ length>> ] bi 10 number-parse boa 0 ; inline
115 : <float-parse> ( i number-parse n -- float-parse i number-parse n )
116 [ drop nip radix>> 0 f float-parse boa ] 3keep ; inline
118 DEFER: @exponent-digit
119 DEFER: @mantissa-digit
125 : @exponent-digit-or-punc ( float-parse i number-parse n char -- float-parse n/f )
127 { CHAR: , [ [ @exponent-digit ] require-next-digit ] }
131 : @exponent-digit ( float-parse i number-parse n char -- float-parse n/f )
132 { float-parse fixnum number-parse integer fixnum } declare
133 digit-in-radix [ [ @exponent-digit-or-punc ] add-digit ] [ @abort ] if ;
135 : @exponent-first-char ( float-parse i number-parse n char -- float-parse n/f )
137 { CHAR: - [ [ @exponent-digit ] require-next-digit ?neg ] }
138 { CHAR: + [ [ @exponent-digit ] require-next-digit ] }
142 : ->exponent ( float-parse i number-parse n -- float-parse' n/f )
143 @split-exponent [ @exponent-first-char ] require-next-digit ?store-exponent ; inline
145 : exponent-char? ( number-parse n char -- number-parse n char ? )
147 { 10 [ dup CHAR: e = [ t ] [ dup CHAR: E = ] if ] }
148 [ drop dup CHAR: p = [ t ] [ dup CHAR: P = ] if ]
151 : or-exponent ( i number-parse n char quot -- n/f )
152 [ exponent-char? [ drop <float-parse> ->exponent ?make-float ] ] dip if ; inline
154 : or-mantissa->exponent ( float-parse i number-parse n char quot -- float-parse n/f )
155 [ exponent-char? [ drop ->exponent ] ] dip if ; inline
157 : @mantissa-digit-or-punc ( float-parse i number-parse n char -- float-parse n/f )
159 { CHAR: , [ [ @mantissa-digit ] require-next-digit ] }
163 : @mantissa-digit ( float-parse i number-parse n char -- float-parse n/f )
164 { float-parse fixnum number-parse integer fixnum } declare
167 [ [ @mantissa-digit-or-punc ] add-mantissa-digit ]
169 ] or-mantissa->exponent ;
171 : ->mantissa ( i number-parse n -- n/f )
172 <float-parse> [ @mantissa-digit ] next-digit ?make-float ; inline
174 : ->required-mantissa ( i number-parse n -- n/f )
175 <float-parse> [ @mantissa-digit ] require-next-digit ?make-float ; inline
177 : @denom-digit-or-punc ( i number-parse n char -- n/f )
179 { CHAR: , [ [ @denom-digit ] require-next-digit ] }
180 { CHAR: . [ ->mantissa ] }
181 [ [ @denom-digit ] or-exponent ]
184 : @denom-digit ( i number-parse n char -- n/f )
185 { fixnum number-parse integer fixnum } declare
186 digit-in-radix [ [ @denom-digit-or-punc ] add-digit ] [ @abort ] if ;
188 : @denom-first-digit ( i number-parse n char -- n/f )
190 { CHAR: . [ ->mantissa ] }
194 : ->denominator ( i number-parse n -- n/f )
195 { fixnum number-parse integer } declare
196 @split [ @denom-first-digit ] require-next-digit ?make-ratio ;
198 : @num-digit-or-punc ( i number-parse n char -- n/f )
200 { CHAR: , [ [ @num-digit ] require-next-digit ] }
201 { CHAR: / [ ->denominator ] }
205 : @num-digit ( i number-parse n char -- n/f )
206 { fixnum number-parse integer fixnum } declare
207 digit-in-radix [ [ @num-digit-or-punc ] add-digit ] [ @abort ] if ;
209 : ->numerator ( i number-parse n -- n/f )
210 { fixnum number-parse integer } declare
211 @split [ @num-digit ] require-next-digit ?add-ratio ;
213 : @pos-digit-or-punc ( i number-parse n char -- n/f )
215 { CHAR: , [ [ @pos-digit ] require-next-digit ] }
216 { CHAR: + [ ->numerator ] }
217 { CHAR: / [ ->denominator ] }
218 { CHAR: . [ ->mantissa ] }
219 [ [ @pos-digit ] or-exponent ]
222 : @pos-digit ( i number-parse n char -- n/f )
223 { fixnum number-parse integer fixnum } declare
224 digit-in-radix [ [ @pos-digit-or-punc ] add-digit ] [ @abort ] if ;
226 : (->radix) ( number-parse radix -- number-parse' )
227 [ [ str>> ] [ length>> ] bi ] dip number-parse boa ; inline
229 : ->radix ( i number-parse n quot radix -- i number-parse n quot )
230 [ (->radix) ] curry 2dip ; inline
232 : with-radix-char ( i number-parse n radix-quot nonradix-quot -- n/f )
235 { CHAR: b [ drop 2 ->radix require-next-digit ] }
236 { CHAR: o [ drop 8 ->radix require-next-digit ] }
237 { CHAR: x [ drop 16 ->radix require-next-digit ] }
238 { f [ 3drop 2drop 0 ] }
239 [ [ drop ] 2dip swap call ]
241 ] 2curry next-digit ; inline
243 : @pos-first-digit ( i number-parse n char -- n/f )
245 { CHAR: . [ ->required-mantissa ] }
246 { CHAR: 0 [ [ @pos-digit ] [ @pos-digit-or-punc ] with-radix-char ] }
250 : @neg-digit-or-punc ( i number-parse n char -- n/f )
252 { CHAR: , [ [ @neg-digit ] require-next-digit ] }
253 { CHAR: - [ ->numerator ] }
254 { CHAR: / [ ->denominator ] }
255 { CHAR: . [ ->mantissa ] }
256 [ [ @neg-digit ] or-exponent ]
259 : @neg-digit ( i number-parse n char -- n/f )
260 { fixnum number-parse integer fixnum } declare
261 digit-in-radix [ [ @neg-digit-or-punc ] add-digit ] [ @abort ] if ;
263 : @neg-first-digit ( i number-parse n char -- n/f )
265 { CHAR: . [ ->required-mantissa ] }
266 { CHAR: 0 [ [ @neg-digit ] [ @neg-digit-or-punc ] with-radix-char ] }
270 : @first-char ( i number-parse n char -- n/f )
272 { CHAR: - [ [ @neg-first-digit ] require-next-digit ?neg ] }
273 { CHAR: + [ [ @pos-first-digit ] require-next-digit ] }
277 : @first-char-no-radix ( i number-parse n char -- n/f )
279 { CHAR: - [ [ @neg-digit ] require-next-digit ?neg ] }
280 { CHAR: + [ [ @pos-digit ] require-next-digit ] }
286 : string>number ( str -- n/f )
287 10 <number-parse> [ @first-char ] require-next-digit ;
289 : base> ( str radix -- n/f )
290 <number-parse> [ @first-char-no-radix ] require-next-digit ;
292 : bin> ( str -- n/f ) 2 base> ; inline
293 : oct> ( str -- n/f ) 8 base> ; inline
294 : dec> ( str -- n/f ) 10 base> ; inline
295 : hex> ( str -- n/f ) 16 base> ; inline
297 : string>digits ( str -- digits )
298 [ digit> ] B{ } map-as ; inline
302 : (digits>integer) ( valid? accum digit radix -- valid? accum )
303 2dup < [ swapd * + ] [ 4drop f 0 ] if ; inline
305 : each-digit ( seq radix quot -- n/f )
306 [ t 0 ] 3dip curry each swap [ drop f ] unless ; inline
310 : digits>integer ( seq radix -- n/f )
311 [ (digits>integer) ] each-digit ; inline
314 dup 10 < [ CHAR: 0 + ] [ 10 - CHAR: a + ] if ; inline
319 48 48 48 48 48 48 48 48 48 48 49 49 49 49 49 49 49 49 49 49
320 50 50 50 50 50 50 50 50 50 50 51 51 51 51 51 51 51 51 51 51
321 52 52 52 52 52 52 52 52 52 52 53 53 53 53 53 53 53 53 53 53
322 54 54 54 54 54 54 54 54 54 54 55 55 55 55 55 55 55 55 55 55
323 56 56 56 56 56 56 56 56 56 56 57 57 57 57 57 57 57 57 57 57
327 48 49 50 51 52 53 54 55 56 57 48 49 50 51 52 53 54 55 56 57
328 48 49 50 51 52 53 54 55 56 57 48 49 50 51 52 53 54 55 56 57
329 48 49 50 51 52 53 54 55 56 57 48 49 50 51 52 53 54 55 56 57
330 48 49 50 51 52 53 54 55 56 57 48 49 50 51 52 53 54 55 56 57
331 48 49 50 51 52 53 54 55 56 57 48 49 50 51 52 53 54 55 56 57
334 : (two-digit) ( num accum -- num' accum )
336 100 /mod [ TENS nth-unsafe ] [ ONES nth-unsafe ] bi
337 ] dip [ push ] keep [ push ] keep ; inline
339 : (one-digit) ( num accum -- num' accum )
340 [ 10 /mod CHAR: 0 + ] dip [ push ] keep ; inline
342 : (bignum>dec) ( num accum -- num' accum )
343 [ over most-positive-fixnum > ]
344 [ { bignum sbuf } declare (two-digit) ] while
345 [ >fixnum ] dip ; inline
347 : (fixnum>dec) ( num accum -- num' accum )
348 { fixnum sbuf } declare
349 [ over 10 >= ] [ (two-digit) ] while
350 [ over zero? ] [ (one-digit) ] until ; inline
352 GENERIC: (positive>dec) ( num -- str )
354 M: bignum (positive>dec)
355 12 <sbuf> (bignum>dec) (fixnum>dec) "" like reverse! nip ; inline
357 : (count-digits) ( digits n -- digits' )
359 { [ dup 10 < ] [ drop ] }
360 { [ dup 100 < ] [ drop 1 fixnum+fast ] }
361 { [ dup 1,000 < ] [ drop 2 fixnum+fast ] }
363 dup 1,000,000,000,000 < [
375 dup 10,000,000,000 < [
376 1,000,000,000 >= 9 8 ?
378 100,000,000,000 >= 11 10 ?
382 [ 12 fixnum+fast ] [ 1,000,000,000,000 /i ] bi*
386 } cond ; inline recursive
388 M: fixnum (positive>dec)
389 1 over (count-digits) <sbuf> (fixnum>dec) "" like reverse! nip ; inline
391 : (positive>base) ( num radix -- str )
392 dup 1 <= [ invalid-radix ] when
393 [ dup 0 > ] swap [ /mod >digit ] curry "" produce-as nip
396 : positive>base ( num radix -- str )
397 dup 10 = [ drop (positive>dec) ] [ (positive>base) ] if ; inline
401 GENERIC# >base 1 ( n radix -- str )
403 : number>string ( n -- str ) 10 >base ; inline
405 : >bin ( n -- str ) 2 >base ; inline
406 : >oct ( n -- str ) 8 >base ; inline
407 : >hex ( n -- str ) 16 >base ; inline
416 [ neg ] dip positive>base CHAR: - prefix
421 [ [ 0 < ] [ abs 1 /mod ] bi ]
422 [ [ positive>base ] curry ] bi*
424 [ [ numerator ] [ denominator ] bi ] dip bi@ "/" glue
425 ] keep rot [ drop ] [
426 swap call pick "-" "+" ? rot 3append
427 ] if-zero swap [ CHAR: - prefix ] when ;
431 : fix-float ( str -- newstr )
432 CHAR: e over member? [
433 "e" split1 [ fix-float ] dip "e" glue
435 CHAR: . over member? [ ".0" append ] unless
438 : mantissa-expt-normalize ( mantissa expt -- mantissa' expt' )
439 [ dup log2 52 swap - [ shift 52 2^ 1 - bitand ] [ 1022 + neg ] bi ]
442 : mantissa-expt ( float -- mantissa expt )
444 [ -0.0 double>bits bitnot bitand -52 shift ] bi
445 mantissa-expt-normalize ;
447 : float>hex-sign ( bits -- str )
448 -0.0 double>bits bitand zero? "" "-" ? ;
450 : float>hex-value ( mantissa -- str )
451 >hex 13 CHAR: 0 pad-head [ CHAR: 0 = ] trim-tail
452 [ "0" ] when-empty "1." prepend ;
454 : float>hex-expt ( mantissa -- str )
455 10 >base "p" prepend ;
457 : float>hex ( n -- str )
460 mantissa-expt [ float>hex-value ] [ float>hex-expt ] bi*
463 : format-string ( format -- format )
464 0 suffix >byte-array ; foldable
466 : format-head ( byte-array n -- string )
467 swap over 0 <string> [
469 [ [ nth-unsafe ] 2keep drop ]
470 [ set-string-nth-fast ] bi*
471 ] 2curry each-integer
474 : format-float ( n format -- string )
475 format-string (format-float)
476 dup [ 0 = ] find drop
477 format-head fix-float ; inline
479 : float>base ( n radix -- str )
482 { 10 [ "%.16g" format-float ] }
490 { [ over fp-nan? ] [ 2drop "0/0." ] }
491 { [ over 1/0. = ] [ 2drop "1/0." ] }
492 { [ over -1/0. = ] [ 2drop "-1/0." ] }
493 { [ over 0.0 fp-bitwise= ] [ 2drop "0.0" ] }
494 { [ over -0.0 fp-bitwise= ] [ 2drop "-0.0" ] }
498 : # ( n -- ) number>string % ; inline