hollerith
Version:
vectorial indices (VDXs) that allow for arbitrary many interstitial elements with a binary sortable representation
273 lines (241 loc) • 14 kB
text/coffeescript
'use strict'
#===========================================================================================================
SFMODULES = require 'bricabrac-single-file-modules'
{ type_of, } = SFMODULES.unstable.require_type_of()
{ show_no_colors: rpr, } = SFMODULES.unstable.require_show()
{ debug, } = console
{ regex, } = require 'regex'
{ freeze, } = Object
{ Type,
Typespace,
CFG, } = SFMODULES.unstable.require_nanotypes()
{ is_positive_integer_power_of,
is_positive_all_niner,
get_max_integer,
# get_max_niner_digit_count,
# encode,
# decode,
# log_to_base,
# get_required_digits,
} = SFMODULES.unstable.require_anybase()
#===========================================================================================================
internals = Object.assign { Type, Typespace, },
#---------------------------------------------------------------------------------------------------------
get_leading_novas_re: ( _nova ) -> ( regex 'g' )""" ^ #{_nova}* (?= .+ $ ) """
#===========================================================================================================
# HOLLERITH TYPESPACE
#===========================================================================================================
class Hollerith_typespace extends Typespace
#---------------------------------------------------------------------------------------------------------
constructor: ( cfg ) ->
super cfg
.validate @[CFG].blank
blank_esc = RegExp.escape @[CFG].blank
blank_splitter = new RegExp "(?:#{blank_esc})+", 'gv'
@[CFG] = freeze { @[CFG]..., blank_splitter, }
return undefined
#=========================================================================================================
@[CFG]:
blank: ' '
#=========================================================================================================
: ( x ) -> ( x is false ) or ( x is true )
: ( x ) -> ( type_of x ) is 'list'
: ( x ) -> ( type_of x ) is 'text'
: ( x ) -> ( .text.isa x ) and ( x.length > 0 )
: ( x ) -> ( .text.isa x ) and ( /^.$/v.test x )
: ( x ) -> Number.isFinite x
: ( x ) -> Number.isSafeInteger x
: ( x ) -> ( .integer.isa x ) and ( x > 0 )
: ( x ) -> ( .integer.isa x ) and ( x >= 0 )
: ( x ) -> .zpinteger.isa x
#---------------------------------------------------------------------------------------------------------
: ( x ) -> x instanceof ( require './main' ).Hollerith
#---------------------------------------------------------------------------------------------------------
### NOTE requiring `x` to be both a character and equal to `@[CFG].blank` means `@[CFG].blank` itself can be tested ###
: ( x ) -> ( .character.isa x ) and ( x is @[CFG].blank )
: ( x ) -> ( .pinteger.isa x )
: ( x ) -> ( .boolean.isa x )
: ( x ) -> ( .pinteger.isa x ) and ( x > 1 )
# : ( x ) -> ( .pinteger.isa x ) and ( x > 1 )
# #---------------------------------------------------------------------------------------------------------
# : ( x ) ->
# return false unless ( .character.isa x )
# placeholder_esc = RegExp.escape x
# _placeholders_re = new RegExp "(?:#{placeholder_esc})+", 'gv'
# { _placeholders_re, }
# return true
# #---------------------------------------------------------------------------------------------------------
# : ( x, { placeholder = null }={} ) ->
# return false unless .text.isa x
# { chrs: ( freeze Array.from x ), }
# return _test_monotony.call @, x, '<', { placeholder, }
#---------------------------------------------------------------------------------------------------------
: ( x ) ->
return false unless .text.isa x
{ chrs: ( freeze Array.from x ), }
return _test_monotony.call @, x, '<'
#---------------------------------------------------------------------------------------------------------
: ( x ) ->
return false unless .text.isa x
{ chrs: ( freeze Array.from x ), }
return _test_monotony.call @, x, '>'
#---------------------------------------------------------------------------------------------------------
: ( x ) -> .incremental_text.dm_isa , null, x
# #---------------------------------------------------------------------------------------------------------
# : ( x, { placeholder = null }={} ) ->
# return .incremental_text.dm_isa , null, x, { placeholder, }
#---------------------------------------------------------------------------------------------------------
: ( x ) ->
return .incremental_text.dm_isa , null, x
#---------------------------------------------------------------------------------------------------------
# : ( x, { cardinals_only = false, _placeholders_re = null }={} ) ->
: ( x, { cardinals_only = false, }={} ) ->
return ( "expected a magnifier, got an empty text" ) unless .nonempty_text.isa x
parts = x.split @[CFG].blank_splitter
if cardinals_only
unless parts.length in [ 1, 2, ]
return ( "magnifiers for { cardinals_only: true } must have 0 or 1 blank, got #{parts.length - 1} blanks")
else
unless parts.length is 2
return ( "magnifiers for { cardinals_only: false } must have exactly 1 blank, got #{parts.length - 1} blanks")
if parts.length is 1
[ nmag_bare_reversed,
pmag_bare, ] = [ null, parts..., ]
else
[ nmag_bare_reversed,
pmag_bare, ] = parts
#.......................................................................................................
if cardinals_only
return ( "Ωbsk___3 ???" ) unless .pmag_bare.dm_isa , { chrs: '_pmag_list', }, pmag_bare # , { _placeholders_re, }
_nmag = null
_pmag = @[CFG].blank + pmag_bare
_nmag_list = null
_pmag_list = freeze Array.from _pmag
#.......................................................................................................
else
return ( "Ωbsk___6 ???" ) unless .nmag_bare_reversed.dm_isa , { chrs: 'nmag_chrs_reversed', }, nmag_bare_reversed
return ( "Ωbsk___7 ???" ) unless .pmag_bare.dm_isa , { chrs: '_pmag_list', }, pmag_bare # , { _placeholders_re, }
return ( "Ωbsk___8 ???" ) unless .incremental_text.isa nmag_bare_reversed + pmag_bare # , { _placeholders_re, }
return ( "Ωbsk___9 ???" ) unless nmag_bare_reversed.length is pmag_bare.length
_nmag = @[CFG].blank + [ .nmag_chrs_reversed..., ].reverse().join ''
_pmag = @[CFG].blank + pmag_bare
_nmag_list = freeze Array.from _nmag
_pmag_list = freeze Array.from _pmag
#.......................................................................................................
{ _nmag, _pmag, _nmag_list, _pmag_list, }
return true
#---------------------------------------------------------------------------------------------------------
: ( x ) ->
return false unless .incremental_text.dm_isa , { chrs: '_digits_list', }, x
_base = ._digits_list.length
return "an digitset must have 2 chrs or more" unless ._base.isa _base
_naught = ._digits_list.at 0
_nova = ._digits_list.at -1
_leading_novas_re = internals.get_leading_novas_re _nova
{ _base, _naught, _nova, _leading_novas_re, }
return true
#---------------------------------------------------------------------------------------------------------
: ( x, { cardinals_only = false, }={} ) ->
return false unless .nonempty_text.isa x
if .character.isa x
_nuns = null
_zpuns = x
_cipher = x
_nuns_list = freeze [] # null !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
_zpuns_list = freeze [ x, ]
{ _nuns, _zpuns, _nuns_list, _zpuns_list, _cipher, }
return true
#.......................................................................................................
parts = x.split @[CFG].blank_splitter
#.......................................................................................................
if cardinals_only
unless parts.length in [ 2, 3, ]
return "uniliterals for { cardinals_only: true } that are not a single character must have 1 or 2 blanks, got #{parts.length - 1} blanks"
else
unless parts.length is 3
return "uniliterals for { cardinals_only: false } that are not a single character must have exactly 2 blanks, got #{parts.length - 1} blanks"
#.......................................................................................................
if parts.length is 2
[ _nuns,
_cipher,
_puns, ] = [ null, parts..., ]
else
[ _nuns,
_cipher,
_puns, ] = parts
_nuns = null if cardinals_only
#.......................................................................................................
_zpuns = _cipher + _puns
{ _nuns, _zpuns, _cipher, }
#.......................................................................................................
if cardinals_only
{ _nuns_list: null, }
else
return false unless .incremental_text.dm_isa , { chrs: '_nuns_list', }, _nuns
#.......................................................................................................
return false unless .incremental_text.dm_isa , { chrs: '_zpuns_list', }, _zpuns
return true
#---------------------------------------------------------------------------------------------------------
: ( x ) ->
return false unless .nonempty_text.dm_isa , null, x
return false unless .incremental_text.dm_isa , null, x
return true
#---------------------------------------------------------------------------------------------------------
: ( x, _base ) ->
return "x not a positive safe integer" unless .pinteger.isa x
return "_base not a safe integer greater than 1" unless ._base.isa _base
return "x not a positive all-niners" unless is_positive_all_niner x, _base
return true
#---------------------------------------------------------------------------------------------------------
: ( x ) ->
switch true
when .integer.isa x
{ type: 'idx' }
return true
when .list.isa x
{ type: 'vdx' }
return true
return "not a list or an integer"
#---------------------------------------------------------------------------------------------------------
: ( x, _min_integer = null, _max_integer = null ) ->
return "#{rpr x} not a safe integer" unless .integer.isa x
return "#{rpr x} not greater or equal #{_min_integer}" if _min_integer? and not ( x >= _min_integer )
return "#{rpr x} not less or equal #{_min_integer}" if _max_integer? and not ( x <= _max_integer )
return true
#---------------------------------------------------------------------------------------------------------
: ( x ) -> ( .list.isa x ) # and ( x.length > 0 )
#---------------------------------------------------------------------------------------------------------
### TAINT should be method of `T._max_integer` ###
create_max_integer: ({ _base, digits_per_idx, }) ->
.validate _base
.validate digits_per_idx
R = Math.min ( get_max_integer Number.MAX_SAFE_INTEGER, _base ), ( ( _base ** digits_per_idx ) - 1 )
.validate R, _base
return R
#---------------------------------------------------------------------------------------------------------
: ( x, _pmag_list = null ) ->
return "#{x} not a positive safe integer" unless .pinteger.isa x
return "#{x} exceeds limit #{_pmag_list.length} set by magnifiers" if _pmag_list? and not ( x <= _pmag_list.length )
return true
#===========================================================================================================
# _test_monotony = ( x, cmp, { placeholder = null }={} ) ->
_test_monotony = ( x, cmp ) ->
{ chrs, } = # = data
return ( "empty is not monotonic" ) if chrs.length is 0
return true if chrs.length is 1
for idx in [ 1 ... chrs.length ]
prv_chr = chrs[ idx - 1 ] # ; continue if placeholder? and prv_chr is placeholder
chr = chrs[ idx ] # ; continue if placeholder? and chr is placeholder
R = switch cmp
when '>' then prv_chr > chr
when '<' then prv_chr < chr
else throw new Error "Ωbsk__10 (internal) expected '>' or '<', got #{rpr cmp}"
continue if R
{ fail: { x, idx, prv_chr, chr, }, }
return false
return true
#===========================================================================================================
Object.assign module.exports, {
Hollerith_typespace,
CFG: CFG,
internals, }