USING: accessors arrays assocs fry hashtables kernel math math.order math.vectors sequences ; IN: mdarray ! the first variant is based on an array, and has less overhead. ! use this when the entire shape will probably be filled. for example, a game board. ! NOTE: in positions, last one is the most specific (1d) TUPLE: multi1 default-value { planes sequence } { underlying array } ; : ( planes default-value -- mdarray ) 2dup [ product ] dip multi1 new swap >>underlying swap >>default-value swap >>planes ; ! the second variant has more overhead, but isn't filled with empty values. ! for example, if you want to mark a route in a map, then only the route will take space. TUPLE: multi2 default-value { planes sequence } { cells hashtable } ; : ( planes default-value -- mdarray ) multi2 new swap >>default-value swap >>planes H{ } clone >>cells ; : within? ( positions planes -- ? ) [ 1 - 0 swap between? ] 2all? ; ERROR: not-within! positions planes ; : ensure-within ( positions seq -- positions seq ) 2dup planes>> within? [ planes>> not-within! ] unless ; M: multi2 set-nth ( elt positions seq -- ) ensure-within cells>> set-at ; M: multi2 nth ( positions seq -- cell/default ) ensure-within [ cells>> at* ] [ default-value>> ] bi '[ drop _ ] unless ; ! { 2 3 5 } => { 15 3 1 } ! { 2 3 3 } => { 9 3 1 } : muls-seq ( planes -- calc-seq ) { 1 } swap rest [ over first * prefix ] each ; : coord>index ( positions planes -- index ) muls-seq v* sum ; : (positions>index) ( positions seq -- index seq ) [ planes>> coord>index ] [ underlying>> ] bi ; M: multi1 nth ( positions seq -- cell ) ensure-within (positions>index) nth ; M: multi1 set-nth ( elt positions seq -- ) ensure-within (positions>index) set-nth ;