text:
#constants
#Scene object types. Values are serialized
sim_object_shape_type           =0
sim_object_joint_type           =1
sim_object_graph_type           =2
sim_object_camera_type          =3
sim_object_dummy_type           =4
sim_object_proximitysensor_type =5
sim_object_reserved1            =6
sim_object_reserved2            =7
sim_object_path_type            =8
sim_object_visionsensor_type    =9
sim_object_volume_type          =10
sim_object_mill_type            =11
sim_object_forcesensor_type     =12
sim_object_light_type           =13
sim_object_mirror_type          =14

#General object types. Values are serialized
sim_appobj_object_type          =109
sim_appobj_collision_type       =110
sim_appobj_distance_type        =111
sim_appobj_simulation_type      =112
sim_appobj_ik_type              =113
sim_appobj_constraintsolver_type=114
sim_appobj_collection_type      =115
sim_appobj_ui_type              =116
sim_appobj_script_type          =117
sim_appobj_pathplanning_type    =118
sim_appobj_RESERVED_type        =119
sim_appobj_texture_type         =120

# Ik calculation methods. Values are serialized
sim_ik_pseudo_inverse_method        =0
sim_ik_damped_least_squares_method  =1
sim_ik_jacobian_transpose_method    =2

# Ik constraints. Values are serialized
sim_ik_x_constraint         =1
sim_ik_y_constraint         =2
sim_ik_z_constraint         =4
sim_ik_alpha_beta_constraint=8
sim_ik_gamma_constraint     =16
sim_ik_avoidance_constraint =64

# Ik calculation results
sim_ikresult_not_performed  =0
sim_ikresult_success        =1
sim_ikresult_fail           =2

# Scene object sub-types. Values are serialized
# Light sub-types
sim_light_omnidirectional_subtype   =1
sim_light_spot_subtype              =2
sim_light_directional_subtype       =3
# Joint sub-types
sim_joint_revolute_subtype          =10
sim_joint_prismatic_subtype         =11
sim_joint_spherical_subtype         =12
# Shape sub-types
sim_shape_simpleshape_subtype       =20
sim_shape_multishape_subtype        =21
# Proximity sensor sub-types
sim_proximitysensor_pyramid_subtype =30
sim_proximitysensor_cylinder_subtype=31
sim_proximitysensor_disc_subtype    =32
sim_proximitysensor_cone_subtype    =33
sim_proximitysensor_ray_subtype     =34
# Mill sub-types
sim_mill_pyramid_subtype            =40
sim_mill_cylinder_subtype           =41
sim_mill_disc_subtype               =42
sim_mill_cone_subtype               =42
# No sub-type
sim_object_no_subtype               =200

#Scene object main properties (serialized)
sim_objectspecialproperty_collidable                    =0x0001
sim_objectspecialproperty_measurable                    =0x0002
#reserved                        =0x0004
#reserved                        =0x0008
sim_objectspecialproperty_detectable_ultrasonic            =0x0010
sim_objectspecialproperty_detectable_infrared            =0x0020
sim_objectspecialproperty_detectable_laser                =0x0040
sim_objectspecialproperty_detectable_inductive            =0x0080
sim_objectspecialproperty_detectable_capacitive            =0x0100
sim_objectspecialproperty_renderable                    =0x0200
sim_objectspecialproperty_detectable_all =sim_objectspecialproperty_detectable_ultrasonic|sim_objectspecialproperty_detectable_infrared|sim_objectspecialproperty_detectable_laser|sim_objectspecialproperty_detectable_inductive|sim_objectspecialproperty_detectable_capacitive
sim_objectspecialproperty_cuttable                        =0x0400
sim_objectspecialproperty_pathplanning_ignored            =0x0800

# Model properties (serialized)
sim_modelproperty_not_collidable                =0x0001
sim_modelproperty_not_measurable                =0x0002
sim_modelproperty_not_renderable                =0x0004
sim_modelproperty_not_detectable                =0x0008
sim_modelproperty_not_cuttable                    =0x0010
sim_modelproperty_not_dynamic                    =0x0020
sim_modelproperty_not_respondable                =0x0040 # cannot be selected if sim_modelproperty_not_dynamic is not selected
sim_modelproperty_not_reset                        =0x0080 # Model is not reset at simulation end. This flag is cleared at simulation end
sim_modelproperty_not_visible                    =0x0100 # Whole model is invisible independent of local visibility settings
sim_modelproperty_not_model                        =0xf000 # object is not a model

# Check the documentation instead of comments below!!
# Following messages are dispatched to the Lua-message container
sim_message_ui_button_state_change  =0    # a UI button slider etc. changed (due to a user's action). aux[0]=UI handle aux[1]=button handle aux[2]=button attributes aux[3]=slider position (if slider)
sim_message_reserved9               =1    # Do not use
sim_message_object_selection_changed=2
sim_message_reserved10                =3    # do not use
sim_message_model_loaded            =4
sim_message_reserved11                =5    # do not use
sim_message_keypress                =6    # a key was pressed while the focus was on a page (aux[0]=key aux[1]=ctrl and shift key state)
sim_message_bannerclicked            =7    # a banner was clicked (aux[0]=banner ID)

# Following messages are dispatched only to the C-API (not available from Lua)
sim_message_for_c_api_only_start        =0x100      # Do not use
sim_message_reserved1                   =0x101      # Do not use
sim_message_reserved2                    =0x102      # Do not use
sim_message_reserved3                    =0x103      # Do not use
sim_message_eventcallback_scenesave        =0x104        # about to save a scene
sim_message_eventcallback_modelsave        =0x105      # about to save a model (current selection will be saved)
sim_message_eventcallback_moduleopen    =0x106        # called when simOpenModule in Lua is called
sim_message_eventcallback_modulehandle    =0x107        # called when simHandleModule in Lua is called with argument false
sim_message_eventcallback_moduleclose    =0x108        # called when simCloseModule in Lua is called
sim_message_reserved4                    =0x109      # Do not use
sim_message_reserved5                    =0x10a        # Do not use
sim_message_reserved6                    =0x10b        # Do not use
sim_message_reserved7                    =0x10c        # Do not use
sim_message_eventcallback_instancepass    =0x10d        # Called once every main application loop pass. auxiliaryData[0] contains event flags of events that happened since last time
sim_message_eventcallback_broadcast     =0x10e
sim_message_eventcallback_imagefilter_enumreset =0x10f
sim_message_eventcallback_imagefilter_enumerate      =0x110
sim_message_eventcallback_imagefilter_adjustparams   =0x111
sim_message_eventcallback_imagefilter_reserved       =0x112
sim_message_eventcallback_imagefilter_process        =0x113
sim_message_eventcallback_reserved1                  =0x114   # do not use
sim_message_eventcallback_reserved2                  =0x115   # do not use
sim_message_eventcallback_reserved3                  =0x116   # do not use
sim_message_eventcallback_reserved4                  =0x117   # do not use
sim_message_eventcallback_abouttoundo                 =0x118   # the undo button was hit and a previous state is about to be restored
sim_message_eventcallback_undoperformed                 =0x119   # the undo button was hit and a previous state restored
sim_message_eventcallback_abouttoredo                 =0x11a   # the redo button was hit and a future state is about to be restored
sim_message_eventcallback_redoperformed                 =0x11b   # the redo button was hit and a future state restored
sim_message_eventcallback_scripticondblclick         =0x11c   # scipt icon was double clicked.  (aux[0]=object handle associated with script set replyData[0] to 1 if script should not be opened)
sim_message_eventcallback_simulationabouttostart     =0x11d
sim_message_eventcallback_simulationended            =0x11e
sim_message_eventcallback_reserved5                     =0x11f   # do not use
sim_message_eventcallback_keypress                     =0x120   # a key was pressed while the focus was on a page (aux[0]=key aux[1]=ctrl and shift key state)
sim_message_eventcallback_modulehandleinsensingpart  =0x121   # called when simHandleModule in Lua is called with argument true
sim_message_eventcallback_renderingpass              =0x122   # called just before the scene is rendered
sim_message_eventcallback_bannerclicked              =0x123   # called when a banner was clicked (aux[0]=banner ID)
sim_message_eventcallback_menuitemselected           =0x124   # auxiliaryData[0] indicates the handle of the item auxiliaryData[1] indicates the state of the item
sim_message_eventcallback_refreshdialogs             =0x125   # aux[0]=refresh degree (0=light 1=medium 2=full)
sim_message_eventcallback_sceneloaded                =0x126
sim_message_eventcallback_modelloaded                =0x127
sim_message_eventcallback_instanceswitch             =0x128
sim_message_eventcallback_guipass                    =0x129
sim_message_eventcallback_mainscriptabouttobecalled  =0x12a
sim_message_eventcallback_rmlposition                =0x12b   #the command simRMLPosition was called. The appropriate plugin should handle the call
sim_message_eventcallback_rmlvelocity                =0x12c   # the command simRMLVelocity was called. The appropriate plugin should handle the call
sim_message_simulation_start_resume_request          =0x1000
sim_message_simulation_pause_request                 =0x1001
sim_message_simulation_stop_request                  =0x1002

# Scene object properties. Combine with the | operator
sim_objectproperty_reserved1                =0x0000
sim_objectproperty_reserved2                =0x0001
sim_objectproperty_reserved3                =0x0002
sim_objectproperty_reserved4                =0x0003
sim_objectproperty_reserved5                =0x0004 # formely sim_objectproperty_visible
sim_objectproperty_reserved6                =0x0008 # formely sim_objectproperty_wireframe
sim_objectproperty_collapsed                =0x0010
sim_objectproperty_selectable                =0x0020
sim_objectproperty_reserved7                =0x0040
sim_objectproperty_selectmodelbaseinstead    =0x0080
sim_objectproperty_dontshowasinsidemodel    =0x0100
# reserved                                    =0x0200
sim_objectproperty_canupdatedna                =0x0400
sim_objectproperty_selectinvisible            =0x0800
sim_objectproperty_depthinvisible            =0x1000

# type of arguments (input and output) for custom lua commands
sim_lua_arg_nil     =0
sim_lua_arg_bool    =1
sim_lua_arg_int     =2
sim_lua_arg_float   =3
sim_lua_arg_string  =4
sim_lua_arg_invalid =5
sim_lua_arg_table   =8

# custom user interface properties. Values are serialized.
sim_ui_property_visible                        =0x0001
sim_ui_property_visibleduringsimulationonly    =0x0002
sim_ui_property_moveable                    =0x0004
sim_ui_property_relativetoleftborder        =0x0008
sim_ui_property_relativetotopborder            =0x0010
sim_ui_property_fixedwidthfont                =0x0020
sim_ui_property_systemblock                    =0x0040
sim_ui_property_settocenter                    =0x0080
sim_ui_property_rolledup                    =0x0100
sim_ui_property_selectassociatedobject        =0x0200
sim_ui_property_visiblewhenobjectselected    =0x0400

# button properties. Values are serialized.
sim_buttonproperty_button                =0x0000
sim_buttonproperty_label                =0x0001
sim_buttonproperty_slider                =0x0002
sim_buttonproperty_editbox                =0x0003
sim_buttonproperty_staydown                =0x0008
sim_buttonproperty_enabled                =0x0010
sim_buttonproperty_borderless            =0x0020
sim_buttonproperty_horizontallycentered    =0x0040
sim_buttonproperty_ignoremouse            =0x0080
sim_buttonproperty_isdown                =0x0100
sim_buttonproperty_transparent            =0x0200
sim_buttonproperty_nobackgroundcolor    =0x0400
sim_buttonproperty_rollupaction            =0x0800
sim_buttonproperty_closeaction            =0x1000
sim_buttonproperty_verticallycentered    =0x2000
sim_buttonproperty_downupevent            =0x4000

# Simulation status
sim_simulation_stopped                        =0x00                                # Simulation is stopped
sim_simulation_paused                        =0x08                                # Simulation is paused
sim_simulation_advancing                    =0x10                                # Simulation is advancing
sim_simulation_advancing_firstafterstop        =sim_simulation_advancing|0x00        # First simulation pass (1x)
sim_simulation_advancing_running            =sim_simulation_advancing|0x01        # Normal simulation pass (>=1x)
# reserved                                    =sim_simulation_advancing|0x02
sim_simulation_advancing_lastbeforepause    =sim_simulation_advancing|0x03        # Last simulation pass before pause (1x)
sim_simulation_advancing_firstafterpause    =sim_simulation_advancing|0x04        # First simulation pass after pause (1x)
sim_simulation_advancing_abouttostop        =sim_simulation_advancing|0x05        # "Trying to stop" simulation pass (>=1x)
sim_simulation_advancing_lastbeforestop        =sim_simulation_advancing|0x06        # Last simulation pass (1x)

# Script execution result (first return value)
sim_script_no_error                    =0
sim_script_main_script_nonexistent    =1
sim_script_main_script_not_called    =2
sim_script_reentrance_error            =4
sim_script_lua_error                =8
sim_script_call_error                =16

 # Script types (serialized!)
sim_scripttype_mainscript   =0
sim_scripttype_childscript  =1
sim_scripttype_jointctrlcallback  =4
sim_scripttype_contactcallback  =5
sim_scripttype_customizationscript  =6
sim_scripttype_generalcallback  =7

# API call error messages
sim_api_errormessage_ignore    =0    # does not memorize nor output errors
sim_api_errormessage_report    =1    # memorizes errors (default for C-API calls)
sim_api_errormessage_output    =2  # memorizes and outputs errors (default for Lua-API calls)

# special argument of some functions
sim_handle_all                        =-2
sim_handle_all_except_explicit        =-3
sim_handle_self                        =-4
sim_handle_main_script                =-5
sim_handle_tree                        =-6
sim_handle_chain                    =-7
sim_handle_single                    =-8
sim_handle_default                    =-9
sim_handle_all_except_self            =-10
sim_handle_parent                    =-11

# special handle flags
sim_handleflag_assembly                =0x400000
sim_handleflag_model                =0x800000

# distance calculation methods (serialized)
sim_distcalcmethod_dl               =0
sim_distcalcmethod_dac              =1
sim_distcalcmethod_max_dl_dac       =2
sim_distcalcmethod_dl_and_dac       =3
sim_distcalcmethod_sqrt_dl2_and_dac2=4
sim_distcalcmethod_dl_if_nonzero    =5
sim_distcalcmethod_dac_if_nonzero   =6

 # Generic dialog styles
sim_dlgstyle_message        =0
sim_dlgstyle_input          =1
sim_dlgstyle_ok             =2
sim_dlgstyle_ok_cancel      =3
sim_dlgstyle_yes_no         =4
sim_dlgstyle_dont_center    =32# can be combined with one of above values. Only with this flag can the position of the related UI be set just after dialog creation

 # Generic dialog return values
sim_dlgret_still_open   =0
sim_dlgret_ok           =1
sim_dlgret_cancel       =2
sim_dlgret_yes          =3
sim_dlgret_no           =4

# Path properties
sim_pathproperty_show_line                            =0x0001
sim_pathproperty_show_orientation                    =0x0002
sim_pathproperty_closed_path                        =0x0004
sim_pathproperty_automatic_orientation                =0x0008
sim_pathproperty_invert_velocity                    =0x0010
sim_pathproperty_infinite_acceleration                =0x0020
sim_pathproperty_flat_path                            =0x0040
sim_pathproperty_show_position                        =0x0080
sim_pathproperty_auto_velocity_profile_translation    =0x0100
sim_pathproperty_auto_velocity_profile_rotation        =0x0200
sim_pathproperty_endpoints_at_zero                    =0x0400
sim_pathproperty_keep_x_up                            =0x0800

 # drawing objects
# following are mutually exclusive
sim_drawing_points          =0            # 3 values per point (point size in pixels)
sim_drawing_lines            =1            # 6 values per line (line size in pixels)
sim_drawing_triangles        =2            # 9 values per triangle
sim_drawing_trianglepoints    =3            # 6 values per point (3 for triangle position 3 for triangle normal vector) (triangle size in meters)
sim_drawing_quadpoints        =4            # 6 values per point (3 for quad position 3 for quad normal vector) (quad size in meters)
sim_drawing_discpoints        =5            # 6 values per point (3 for disc position 3 for disc normal vector) (disc size in meters)
sim_drawing_cubepoints        =6          # 6 values per point (3 for cube position 3 for cube normal vector) (cube size in meters)
sim_drawing_spherepoints    =7          # 3 values per point (sphere size in meters)

# following can be or-combined
sim_drawing_itemcolors                =0x00020 # +3 values per item (each item has its own ambient color (rgb values)).
                                             # Mutually exclusive with sim_drawing_vertexcolors
sim_drawing_vertexcolors            =0x00040 # +3 values per vertex (each vertex has its own ambient color (rgb values). Only for sim_drawing_lines (+6) and for sim_drawing_triangles(+9)). Mutually exclusive with sim_drawing_itemcolors
sim_drawing_itemsizes                =0x00080 # +1 value per item (each item has its own size). Not for sim_drawing_triangles
sim_drawing_backfaceculling            =0x00100 # back faces are not displayed for all items
sim_drawing_wireframe                =0x00200 # all items displayed in wireframe
sim_drawing_painttag                =0x00400 # all items are tagged as paint (for additinal processing at a later stage)
sim_drawing_followparentvisibility    =0x00800 # if the object is associated with a scene object then it follows that visibility otherwise it is always visible
sim_drawing_cyclic                    =0x01000 # if the max item count was reached then the first items are overwritten.
sim_drawing_50percenttransparency    =0x02000 # the drawing object will be 50% transparent
sim_drawing_25percenttransparency    =0x04000 # the drawing object will be 25% transparent
sim_drawing_12percenttransparency    =0x08000 # the drawing object will be 12.5% transparent
sim_drawing_emissioncolor            =0x10000 # When used in combination with sim_drawing_itemcolors or sim_drawing_vertexcolors then the specified colors will be for the emissive component
sim_drawing_facingcamera            =0x20000 # Only for trianglepoints quadpoints discpoints and cubepoints. If specified the normal verctor is calculated to face the camera (each item data requires 3 values less)
sim_drawing_overlay                    =0x40000 # When specified objects are always drawn on top of "regular objects"
sim_drawing_itemtransparency        =0x80000  # +1 value per item (each item has its own transparency value (0-1)). Not compatible with sim_drawing_vertexcolors

# banner values
# following can be or-combined
sim_banner_left                        =0x00001 # Banners display on the left of the specified point
sim_banner_right                    =0x00002 # Banners display on the right of the specified point
sim_banner_nobackground                =0x00004 # Banners have no background rectangle
sim_banner_overlay                    =0x00008 # When specified banners are always drawn on top of "regular objects"
sim_banner_followparentvisibility    =0x00010 # if the object is associated with a scene object then it follows that visibility otherwise it is always visible
sim_banner_clickselectsparent        =0x00020 # if the object is associated with a scene object then clicking the banner will select the scene object
sim_banner_clicktriggersevent        =0x00040 # if the banner is clicked an event is triggered (sim_message_eventcallback_bannerclicked and sim_message_bannerclicked are generated)
sim_banner_facingcamera                =0x00080 # If specified the banner will always face the camera by rotating around the banner's vertical axis (y-axis)
sim_banner_fullyfacingcamera        =0x00100 # If specified the banner will always fully face the camera (the banner's orientation is same as the camera looking at it)
sim_banner_backfaceculling            =0x00200 # If specified the banner will only be visible from one side
sim_banner_keepsamesize                =0x00400 # If specified the banner will always appear in the same size. In that case size represents the character height in pixels
sim_banner_bitmapfont                =0x00800 # If specified a fixed-size bitmap font is used. The text will also always fully face the camera and be right
                                             # to the specified position. Bitmap fonts are not clickable

# particle objects following are mutually exclusive
sim_particle_points1        =0  # 6 values per point (pt1 and pt2. Pt1 is start position pt2-pt1 is the initial velocity vector). i
                                #Point is 1 pixel big. Only appearance is a point internally handled as a perfect sphere
sim_particle_points2        =1    # 6 values per point. Point is 2 pixel big. Only appearance is a point internally handled as a perfect sphere
sim_particle_points4        =2    # 6 values per point. Point is 4 pixel big. Only appearance is a point internally handled as a perfect sphere
sim_particle_roughspheres    =3    # 6 values per sphere. Only appearance is rough. Internally a perfect sphere
sim_particle_spheres        =4    # 6 values per sphere. Internally a perfect sphere

# following can be or-combined
sim_particle_respondable1to4        =0x0020 # the particles are respondable against shapes (against all objects that have at least one bit 1-4 activated in the global respondable mask)
sim_particle_respondable5to8        =0x0040 # the particles are respondable against shapes (against all objects that have at least one bit 5-8 activated in the global respondable mask)
sim_particle_particlerespondable    =0x0080 # the particles are respondable against each other
sim_particle_ignoresgravity            =0x0100 # the particles ignore the effect of gravity. Not compatible with sim_particle_water
sim_particle_invisible                =0x0200 # the particles are invisible
sim_particle_itemsizes                =0x0400 # +1 value per particle (each particle can have a different size)
sim_particle_itemdensities            =0x0800 # +1 value per particle (each particle can have a different density)
sim_particle_itemcolors                =0x1000 # +3 values per particle (each particle can have a different color)
sim_particle_cyclic                    =0x2000 # if the max item count was reached then the first items are overwritten.
sim_particle_emissioncolor            =0x4000 # When used in combination with sim_particle_itemcolors then the specified colors will be for the emissive component
sim_particle_water                    =0x8000 # the particles are water particles (no weight in the water (i.e. when z<0)). Not compatible with sim_particle_ignoresgravity
sim_particle_painttag                =0x10000 # The particles can be seen by vision sensors (sim_particle_invisible must not be set)

# custom user interface menu attributes
sim_ui_menu_title        =1
sim_ui_menu_minimize    =2
sim_ui_menu_close        =4
sim_ui_menu_systemblock =8

# Boolean parameters
sim_boolparam_hierarchy_visible                 =0
sim_boolparam_console_visible                   =1
sim_boolparam_collision_handling_enabled        =2
sim_boolparam_distance_handling_enabled         =3
sim_boolparam_ik_handling_enabled               =4
sim_boolparam_gcs_handling_enabled              =5
sim_boolparam_dynamics_handling_enabled         =6
sim_boolparam_joint_motion_handling_enabled     =7
sim_boolparam_path_motion_handling_enabled      =8
sim_boolparam_proximity_sensor_handling_enabled =9
sim_boolparam_vision_sensor_handling_enabled    =10
sim_boolparam_mill_handling_enabled             =11
sim_boolparam_browser_visible                   =12
sim_boolparam_scene_and_model_load_messages     =13
sim_reserved0                                   =14
sim_boolparam_shape_textures_are_visible        =15
sim_boolparam_display_enabled                   =16
sim_boolparam_infotext_visible                  =17
sim_boolparam_statustext_open                   =18
sim_boolparam_fog_enabled                       =19
sim_boolparam_rml2_available                    =20
sim_boolparam_rml4_available                    =21
sim_boolparam_mirrors_enabled                    =22
sim_boolparam_aux_clip_planes_enabled            =23
sim_boolparam_full_model_copy_from_api            =24
sim_boolparam_realtime_simulation                =25
sim_boolparam_force_show_wireless_emission        =27
sim_boolparam_force_show_wireless_reception        =28
sim_boolparam_video_recording_triggered            =29
sim_boolparam_threaded_rendering_enabled        =32
sim_boolparam_fullscreen                        =33
sim_boolparam_headless                            =34
sim_boolparam_hierarchy_toolbarbutton_enabled    =35
sim_boolparam_browser_toolbarbutton_enabled        =36
sim_boolparam_objectshift_toolbarbutton_enabled    =37
sim_boolparam_objectrotate_toolbarbutton_enabled=38
sim_boolparam_force_calcstruct_all_visible        =39
sim_boolparam_force_calcstruct_all                =40
sim_boolparam_exit_request                        =41
sim_boolparam_play_toolbarbutton_enabled        =42
sim_boolparam_pause_toolbarbutton_enabled        =43
sim_boolparam_stop_toolbarbutton_enabled        =44
sim_boolparam_waiting_for_trigger                =45

# Integer parameters
sim_intparam_error_report_mode      =0  # Check sim_api_errormessage_... constants above for valid values
sim_intparam_program_version        =1  # e.g Version 2.1.4 --> 20104. Can only be read
sim_intparam_instance_count         =2  # do not use anymore (always returns 1 since V-REP 2.5.11)
sim_intparam_custom_cmd_start_id    =3  # can only be read
sim_intparam_compilation_version    =4  # 0=evaluation version 1=full version 2=player version. Can only be read
sim_intparam_current_page           =5
sim_intparam_flymode_camera_handle  =6  # can only be read
sim_intparam_dynamic_step_divider   =7  # can only be read
sim_intparam_dynamic_engine         =8  # 0=Bullet 1=ODE. 2=Vortex.
sim_intparam_server_port_start      =9  # can only be read
sim_intparam_server_port_range      =10 # can only be read
sim_intparam_visible_layers         =11
sim_intparam_infotext_style         =12
sim_intparam_settings               =13
sim_intparam_edit_mode_type         =14 # can only be read
sim_intparam_server_port_next       =15 # is initialized at sim_intparam_server_port_start
sim_intparam_qt_version             =16 # version of the used Qt framework
sim_intparam_event_flags_read       =17 # can only be read
sim_intparam_event_flags_read_clear =18 # can only be read
sim_intparam_platform               =19 # can only be read
sim_intparam_scene_unique_id        =20 # can only be read
sim_intparam_work_thread_count      =21
sim_intparam_mouse_x                =22
sim_intparam_mouse_y                =23
sim_intparam_core_count             =24
sim_intparam_work_thread_calc_time_ms =25
sim_intparam_idle_fps               =26
sim_intparam_prox_sensor_select_down =27
sim_intparam_prox_sensor_select_up  =28
sim_intparam_stop_request_counter   =29
sim_intparam_program_revision       =30
sim_intparam_mouse_buttons          =31
sim_intparam_dynamic_warning_disabled_mask =32
sim_intparam_simulation_warning_disabled_mask =33
sim_intparam_scene_index            =34
sim_intparam_motionplanning_seed    =35
sim_intparam_speedmodifier          =36

# Float parameters
sim_floatparam_rand=0 # random value (0.0-1.0)
sim_floatparam_simulation_time_step =1
sim_floatparam_stereo_distance        =2

# String parameters
sim_stringparam_application_path=0 # path of V-REP's executable
sim_stringparam_video_filename=1
sim_stringparam_app_arg1            =2
sim_stringparam_app_arg2            =3
sim_stringparam_app_arg3            =4
sim_stringparam_app_arg4            =5
sim_stringparam_app_arg5            =6
sim_stringparam_app_arg6            =7
sim_stringparam_app_arg7            =8
sim_stringparam_app_arg8            =9
sim_stringparam_app_arg9            =10
sim_stringparam_scene_path_and_name    =13

# Array parameters
sim_arrayparam_gravity          =0
sim_arrayparam_fog              =1
sim_arrayparam_fog_color        =2
sim_arrayparam_background_color1=3
sim_arrayparam_background_color2=4
sim_arrayparam_ambient_light    =5
sim_arrayparam_random_euler        =6

sim_objintparam_visibility_layer= 10
sim_objfloatparam_abs_x_velocity= 11
sim_objfloatparam_abs_y_velocity= 12
sim_objfloatparam_abs_z_velocity= 13
sim_objfloatparam_abs_rot_velocity= 14
sim_objfloatparam_objbbox_min_x= 15
sim_objfloatparam_objbbox_min_y= 16
sim_objfloatparam_objbbox_min_z= 17
sim_objfloatparam_objbbox_max_x= 18
sim_objfloatparam_objbbox_max_y= 19
sim_objfloatparam_objbbox_max_z= 20
sim_objfloatparam_modelbbox_min_x= 21
sim_objfloatparam_modelbbox_min_y= 22
sim_objfloatparam_modelbbox_min_z= 23
sim_objfloatparam_modelbbox_max_x= 24
sim_objfloatparam_modelbbox_max_y= 25
sim_objfloatparam_modelbbox_max_z= 26
sim_objintparam_collection_self_collision_indicator= 27
sim_objfloatparam_transparency_offset= 28
sim_objintparam_child_role= 29
sim_objintparam_parent_role= 30
sim_objintparam_manipulation_permissions= 31
sim_objintparam_illumination_handle= 32

sim_visionfloatparam_near_clipping= 1000
sim_visionfloatparam_far_clipping= 1001
sim_visionintparam_resolution_x= 1002
sim_visionintparam_resolution_y= 1003
sim_visionfloatparam_perspective_angle= 1004
sim_visionfloatparam_ortho_size= 1005
sim_visionintparam_disabled_light_components= 1006
sim_visionintparam_rendering_attributes= 1007
sim_visionintparam_entity_to_render= 1008
sim_visionintparam_windowed_size_x= 1009
sim_visionintparam_windowed_size_y= 1010
sim_visionintparam_windowed_pos_x= 1011
sim_visionintparam_windowed_pos_y= 1012
sim_visionintparam_pov_focal_blur= 1013
sim_visionfloatparam_pov_blur_distance= 1014
sim_visionfloatparam_pov_aperture= 1015
sim_visionintparam_pov_blur_sampled= 1016
sim_visionintparam_render_mode= 1017

sim_jointintparam_motor_enabled= 2000
sim_jointintparam_ctrl_enabled= 2001
sim_jointfloatparam_pid_p= 2002
sim_jointfloatparam_pid_i= 2003
sim_jointfloatparam_pid_d= 2004
sim_jointfloatparam_intrinsic_x= 2005
sim_jointfloatparam_intrinsic_y= 2006
sim_jointfloatparam_intrinsic_z= 2007
sim_jointfloatparam_intrinsic_qx= 2008
sim_jointfloatparam_intrinsic_qy= 2009
sim_jointfloatparam_intrinsic_qz= 2010
sim_jointfloatparam_intrinsic_qw= 2011
sim_jointfloatparam_velocity= 2012
sim_jointfloatparam_spherical_qx= 2013
sim_jointfloatparam_spherical_qy= 2014
sim_jointfloatparam_spherical_qz= 2015
sim_jointfloatparam_spherical_qw= 2016
sim_jointfloatparam_upper_limit= 2017
sim_jointfloatparam_kc_k= 2018
sim_jointfloatparam_kc_c= 2019
sim_jointfloatparam_ik_weight= 2021
sim_jointfloatparam_error_x= 2022
sim_jointfloatparam_error_y= 2023
sim_jointfloatparam_error_z= 2024
sim_jointfloatparam_error_a= 2025
sim_jointfloatparam_error_b= 2026
sim_jointfloatparam_error_g= 2027
sim_jointfloatparam_error_pos= 2028
sim_jointfloatparam_error_angle= 2029
sim_jointintparam_velocity_lock= 2030
sim_jointintparam_vortex_dep_handle= 2031
sim_jointfloatparam_vortex_dep_multiplication= 2032
sim_jointfloatparam_vortex_dep_offset= 2033

sim_shapefloatparam_init_velocity_x= 3000
sim_shapefloatparam_init_velocity_y= 3001
sim_shapefloatparam_init_velocity_z= 3002
sim_shapeintparam_static= 3003
sim_shapeintparam_respondable= 3004
sim_shapefloatparam_mass= 3005
sim_shapefloatparam_texture_x= 3006
sim_shapefloatparam_texture_y= 3007
sim_shapefloatparam_texture_z= 3008
sim_shapefloatparam_texture_a= 3009
sim_shapefloatparam_texture_b= 3010
sim_shapefloatparam_texture_g= 3011
sim_shapefloatparam_texture_scaling_x= 3012
sim_shapefloatparam_texture_scaling_y= 3013
sim_shapeintparam_culling= 3014
sim_shapeintparam_wireframe= 3015
sim_shapeintparam_compound= 3016
sim_shapeintparam_convex= 3017
sim_shapeintparam_convex_check= 3018
sim_shapeintparam_respondable_mask= 3019
sim_shapefloatparam_init_velocity_a= 3020
sim_shapefloatparam_init_velocity_b= 3021
sim_shapefloatparam_init_velocity_g= 3022
sim_shapestringparam_color_name= 3023
sim_shapeintparam_edge_visibility= 3024
sim_shapefloatparam_shading_angle= 3025
sim_shapefloatparam_edge_angle= 3026
sim_shapeintparam_edge_borders_hidden= 3027

sim_proxintparam_ray_invisibility= 4000

sim_forcefloatparam_error_x= 5000
sim_forcefloatparam_error_y= 5001
sim_forcefloatparam_error_z= 5002
sim_forcefloatparam_error_a= 5003
sim_forcefloatparam_error_b= 5004
sim_forcefloatparam_error_g= 5005
sim_forcefloatparam_error_pos= 5006
sim_forcefloatparam_error_angle= 5007

sim_lightintparam_pov_casts_shadows= 8000

sim_cameraintparam_disabled_light_components= 9000
sim_camerafloatparam_perspective_angle= 9001
sim_camerafloatparam_ortho_size= 9002
sim_cameraintparam_rendering_attributes= 9003
sim_cameraintparam_pov_focal_blur= 9004
sim_camerafloatparam_pov_blur_distance= 9005
sim_camerafloatparam_pov_aperture= 9006
sim_cameraintparam_pov_blur_samples= 9007

sim_dummyintparam_link_type= 10000

sim_mirrorfloatparam_width= 12000
sim_mirrorfloatparam_height= 12001
sim_mirrorfloatparam_reflectance= 12002
sim_mirrorintparam_enable= 12003

sim_pplanfloatparam_x_min= 20000
sim_pplanfloatparam_x_range= 20001
sim_pplanfloatparam_y_min= 20002
sim_pplanfloatparam_y_range= 20003
sim_pplanfloatparam_z_min= 20004
sim_pplanfloatparam_z_range= 20005
sim_pplanfloatparam_delta_min= 20006
sim_pplanfloatparam_delta_range= 20007

sim_mplanintparam_nodes_computed= 25000
sim_mplanintparam_prepare_nodes= 25001
sim_mplanintparam_clear_nodes= 25002

# User interface elements
sim_gui_menubar                        =0x0001
sim_gui_popups                        =0x0002
sim_gui_toolbar1                    =0x0004
sim_gui_toolbar2                    =0x0008
sim_gui_hierarchy                    =0x0010
sim_gui_infobar                        =0x0020
sim_gui_statusbar                    =0x0040
sim_gui_scripteditor                =0x0080
sim_gui_scriptsimulationparameters    =0x0100
sim_gui_dialogs                        =0x0200
sim_gui_browser                        =0x0400
sim_gui_all                            =0xffff

# Joint modes
sim_jointmode_passive       =0
sim_jointmode_motion        =1
sim_jointmode_ik            =2
sim_jointmode_ikdependent   =3
sim_jointmode_dependent     =4
sim_jointmode_force         =5

# Navigation and selection modes with the mouse. Lower byte values are mutually exclusive upper byte bits can be combined
sim_navigation_passive                    =0x0000
sim_navigation_camerashift                =0x0001
sim_navigation_camerarotate                =0x0002
sim_navigation_camerazoom                =0x0003
sim_navigation_cameratilt                =0x0004
sim_navigation_cameraangle                =0x0005
sim_navigation_camerafly                =0x0006
sim_navigation_objectshift                =0x0007
sim_navigation_objectrotate                =0x0008
sim_navigation_reserved2                =0x0009
sim_navigation_reserved3                =0x000A
sim_navigation_jointpathtest            =0x000B
sim_navigation_ikmanip                    =0x000C
sim_navigation_objectmultipleselection    =0x000D
# Bit-combine following values and add them to one of above's values for a valid navigation mode
sim_navigation_reserved4                =0x0100
sim_navigation_clickselection            =0x0200
sim_navigation_ctrlselection            =0x0400
sim_navigation_shiftselection            =0x0800
sim_navigation_camerazoomwheel            =0x1000
sim_navigation_camerarotaterightbutton    =0x2000

#Remote API constants
SIMX_VERSION                    =0
# Remote API message header structure
SIMX_HEADER_SIZE                =18
simx_headeroffset_crc           =0    # 1 simxUShort. Generated by the client or server. The CRC for the message
simx_headeroffset_version       =2    # 1 byte. Generated by the client or server. The version of the remote API software
simx_headeroffset_message_id    =3    # 1 simxInt. Generated by the client (and used in a reply by the server)
simx_headeroffset_client_time   =7    # 1 simxInt. Client time stamp generated by the client (and sent back by the server)
simx_headeroffset_server_time   =11    # 1 simxInt. Generated by the server when a reply is generated. The server timestamp
simx_headeroffset_scene_id      =15    # 1 simxUShort. Generated by the server. A unique ID identifying the scene currently displayed
simx_headeroffset_server_state  =17    # 1 byte. Generated by the server. Bit coded 0 set --> simulation not stopped 1 set --> simulation paused 2 set --> real-time switch on 3-5 edit mode type (0=no edit mode 1=triangle 2=vertex 3=edge 4=path 5=UI)

# Remote API command header
SIMX_SUBHEADER_SIZE                 =26
simx_cmdheaderoffset_mem_size       =0    # 1 simxInt. Generated by the client or server. The buffer size of the command.
simx_cmdheaderoffset_full_mem_size  =4    # 1 simxInt. Generated by the client or server. The full buffer size of the command (applies to split chunks).
simx_cmdheaderoffset_pdata_offset0  =8    # 1 simxUShort. Generated by the client or server. The amount of data that is part of the command identification.
simx_cmdheaderoffset_pdata_offset1  =10    # 1 simxInt. Generated by the client or server. The amount of shift of the pure data buffer (applies to split chunks).
simx_cmdheaderoffset_cmd=14    # 1 simxInt. Generated by the client (and used in a reply by the server). The command combined with the operation mode of the command.
simx_cmdheaderoffset_delay_or_split =18    # 1 simxUShort. Generated by the client or server. The amount of delay in ms of a continuous command or the max. pure data size to send at once (applies to split commands).
simx_cmdheaderoffset_sim_time       =20    # 1 simxInt. Generated by the server. The simulation time (in ms) when the command was executed (or 0 if simulation is not running)
simx_cmdheaderoffset_status         =24    # 1 byte. Generated by the server. (1 bit 0 is set --> error in function execution on server side). The client writes bit 1 if command cannot be overwritten
simx_cmdheaderoffset_reserved       =25    # 1 byte. Not yet used

# Regular operation modes
simx_opmode_oneshot                =0x000000 # sends command as one chunk. Reply will also come as one chunk. Doesn't wait for the reply.
simx_opmode_blocking            =0x010000 # sends command as one chunk. Reply will also come as one chunk. Waits for the reply (_REPLY_WAIT_TIMEOUT_IN_MS is the timeout).
simx_opmode_oneshot_wait        =0x010000 # sends command as one chunk. Reply will also come as one chunk. Waits for the reply (_REPLY_WAIT_TIMEOUT_IN_MS is the timeout).
simx_opmode_continuous            =0x020000
simx_opmode_streaming            =0x020000 # sends command as one chunk. Command will be stored on the server and always executed
                                                      #(every x ms (as far as possible) where x can be 0-65535. just add x to opmode_continuous).
                                                      # A reply will be sent continuously each time as one chunk. Doesn't wait for the reply.

# Operation modes for heavy data
simx_opmode_oneshot_split        =0x030000   # sends command as several chunks (max chunk size is x bytes where x can be _MIN_SPLIT_AMOUNT_IN_BYTES-65535. Just add x to opmode_oneshot_split). Reply will also come as several chunks. Doesn't wait for the reply.
simx_opmode_continuous_split    =0x040000
simx_opmode_streaming_split    =0x040000    # sends command as several chunks (max chunk size is x bytes where x can be _MIN_SPLIT_AMOUNT_IN_BYTES-65535. Just add x to opmode_continuous_split). Command will be stored on the server and always executed. A reply will be sent continuously each time as several chunks. Doesn't wait for the reply.

# Special operation modes
simx_opmode_discontinue            =0x050000    # removes and cancels all commands stored on the client or server side (also continuous commands)
simx_opmode_buffer                =0x060000    # doesn't send anything but checks if a reply for the given command is available in the input buffer (i.e. previously received from the server)
simx_opmode_remove                =0x070000    # doesn't send anything and doesn't return any specific value. It just erases a similar command reply in the inbox (to free some memory)

# Command return codes
simx_return_ok                    =0x000000
simx_return_novalue_flag        =0x000001        # input buffer doesn't contain the specified command
simx_return_timeout_flag        =0x000002        # command reply not received in time for opmode_oneshot_wait operation mode
simx_return_illegal_opmode_flag    =0x000004        # command doesn't support the specified operation mode
simx_return_remote_error_flag    =0x000008        # command caused an error on the server side
simx_return_split_progress_flag    =0x000010        # previous similar command not yet fully processed (applies to opmode_oneshot_split operation modes)
simx_return_local_error_flag    =0x000020        # command caused an error on the client side
simx_return_initialize_error_flag    =0x000040        # simxStart was not yet called

# Following for backward compatibility (same as above)
simx_error_noerror                =0x000000
simx_error_novalue_flag            =0x000001        # input buffer doesn't contain the specified command
simx_error_timeout_flag            =0x000002        # command reply not received in time for opmode_oneshot_wait operation mode
simx_error_illegal_opmode_flag    =0x000004        # command doesn't support the specified operation mode
simx_error_remote_error_flag    =0x000008        # command caused an error on the server side
simx_error_split_progress_flag    =0x000010        # previous similar command not yet fully processed (applies to opmode_oneshot_split operation modes)
simx_error_local_error_flag        =0x000020        # command caused an error on the client side
simx_error_initialize_error_flag        =0x000040        # simxStart was not yet called


text:
struct CachedSurface
    name::String
    index::Int
end

Base.show(io::IO, surface::CachedSurface) = print(io, surface.name)
_name(s::CachedSurface) = s.name

struct CachedResult
    value::Float64
    signint::Int
    index::Int
end

value(result::CachedResult) = result.value

flipsign(x::CachedResult) = CachedResult(-1*x.value, -1*x.signint, x.index)

function normal(result::CachedResult, cachednormals, ind)
    return result.signint*cachednormals[ind][result.index]
end

function evaluate(surface::CachedSurface, cachedvalues, ind)
    val = cachedvalues[ind][surface.index]
    return CachedResult(val, 1, surface.index)
end

function normal(surface::CachedSurface, cachednormals, ind)
    return cachednormals[ind][surface.index]
end

# complement = -1*
complement(x::CachedResult) = flipsign(x)

# union = min
Base.min(x::CachedResult, y::CachedResult) = ifelse(isless(x.value, y.value), x, y)
union(x::CachedResult, y::CachedResult) = ifelse(isless(x.value, y.value), x, y)

# intersection = max
Base.max(x::CachedResult, y::CachedResult) = ifelse(isless(y.value, x.value), x, y)
intersection(x::CachedResult, y::CachedResult) = ifelse(isless(y.value, x.value), x, y)

# subtraction f,g = f union complement(g)
subtraction(x::CachedResult, y::CachedResult) = intersection(x, complement(y))

struct CachedCSGNode <: AbstractCSGNode
    data::Union{CachedSurface,Symbol}
    children::Array{CachedCSGNode,1}
    sym::Symbol
end

AbstractTrees.children(tree::CachedCSGNode) = tree.children
#AbstractTrees.printnode(io::IO, tree::CachedCSGNode) = print(io, tree.data," ", tree.sym)
AbstractTrees.printnode(io::IO, tree::CachedCSGNode) = print(io, tree.data)

function cachenodes(surfaces, points)
    cached_values = [[value(evaluate(f, p)) for f in surfaces] for p in points]
    cachedsurf = [CachedSurface(_name(surfaces[i])*"$i", i) for i in eachindex(surfaces)]
    nodes = [CachedCSGNode(s, [], gensym()) for s in cachedsurf]
    cached_normals = [[normal(f, p) for f in surfaces] for p in points]
    return (nodes, cached_values, cached_normals)
end

function makecached(surf, n)
    if n == 0 || rand() > 0.7
        return rand(surf)
    else
        # more recursive calls
        op = rand(CSGopsyms)
        s = gensym()
        if op === :complement
            return CachedCSGNode(op, [makecached(surf, n-1)], s)
        else
            return CachedCSGNode(op, [makecached(surf, n-1), makecached(surf, n-1)], s)
        end
    end
end

function randomcachedtree(nodes, maxdepth::Int)
    @assert maxdepth > 0 "Maximum depth should be at least 1!"
    return makecached(nodes, maxdepth)
end

function mapcache(tree, surf)
    if isempty(tree.children)
        return CSGNode(surf[tree.data.index], [])
    else
        op = tree.data
        cs = tree.children
        if op === :complement
            return CSGNode(complement, [mapcache(cs[1], surf)])
        else
            return CSGNode(opDict[op], [mapcache(cs[1], surf), mapcache(cs[2], surf)])
        end
    end
end

function cached2normaltree(tree::CachedCSGNode, surfaces)
    return mapcache(tree, surfaces)
end

function evaluate(tree::CachedCSGNode, cachedcoords, pointind)
    # tree node doesn't have children
    isempty(tree.children) && return evaluate(tree.data, cachedcoords, pointind)

    # node.data isa Function -> it has children
    setop = opDict[tree.data]::Function
    return setop(tree.children..., cachedcoords, pointind)
end

function valueandnormal(tree::CachedCSGNode, ccoords, cnormals, ind)
    val = evaluate(tree, ccoords, ind)
    n = cnormals[ind][val.index]
    return (value(val), n*val.signint)
end

ext: jl


text:
"""
`module Lsq`

This sub-modules contains code to convert a database `LsqDB` into
linear LSQ systems, apply suitable weights, solve the resulting systems
and return an IP (e.g. an NBodyIP).

The ordering of observations (entries of Y, row-indices of Psi) is given by
the loop ordering
```
for ct in configtypes
   for dt in datatypes  # observation types
      for dat in db.data_groups[ct]
      # or: db.kron_groups[ct][dt][:,:,...]
```
E.g., if the configtypes are "E", "F", then
```
Y = [E, E, E, E...., F, F, F, F, ...]
```
"""
module Lsq

import JuLIP, IPFitting, StatsBase

using StaticArrays
using JuLIP: AbstractCalculator, Atoms
using JuLIP.Potentials: OneBody
using IPFitting: Dat, LsqDB, weighthook, observations,
                      observation, hasobservation, eval_obs, vec_obs
using IPFitting.Data: configtype
using IPFitting.DB: dbpath, _nconfigs, matrows

using LinearAlgebra: lmul!, Diagonal, qr, cond, norm, svd
using InteractiveUtils: versioninfo
using LowRankApprox

const Err = IPFitting.Errors

export lsqfit, onb

function _random_subset(db::LsqDB, configtype::AbstractString, p::Real)
   @assert 0 <= p <= 1
   # number of configurations in db for this configtype
   nconfigs = _nconfigs(db, configtype)
   # number of samples we want
   nsamples = ceil(Int, p * nconfigs)
   # draw a random subset
   return StatsBase.sample(1:nconfigs, nsamples, replace = false, ordered = true)
end

"""
`observations(db::LsqDB, configweights::Dict, obsweights::Dict, Vref) -> Vector{Float64}`

construct a vector of observations from the database `db`, specifically from
`db.data`. Only those configurations are considered whose config_type is
in `keys(configweights)` and only those datatypes are loaded whose
ids are in `keys(dataweights)`.
"""
function collect_observations(db::LsqDB,
                              configweights::Dict,
                              obsweights::Dict, Vref )

   nrows = size(db.Ψ, 1)  # total number of observations if we collect
                          # everything in the database
   Y = zeros(Float64, nrows)
   W = zeros(Float64, nrows)
   Icfg = zeros(Int, nrows)
   for (obskey, dat, icfg) in observations(db)  # obskey ∈ {"E","F",...}; d::Dat
      if !haskey(obsweights, obskey)
         continue
      end
      irows = matrows(dat, obskey)
      ct = configtype(dat)
      if !haskey(configweights, ct) #check that we want to fit this configuration
         continue
      end

      # ----- Observation ------
      obs = observation(obskey, dat)
      # If Vref != nothing the it is a calculator and we can subtract
      # its value => this allows us to fit from a reference potential
      if haskey(dat.info, "Vref")
         obs = obs - dat.info["Vref"][obskey]
      elseif Vref != nothing
         obs = obs - vec_obs(obskey, eval_obs(obskey, Vref, Atoms(dat)))
      end
      Y[irows] .= obs
      Icfg[irows] .= icfg
      # ------ Weights --------
      # modify the weights from extra information in the dat structure
      W[irows] .= _get_weights(configweights[ct],
                               obsweights[obskey],
                               weighthook(obskey, dat),
                               dat, obskey, obs)
   end
   return Y, W, Icfg
end

function _get_weights(ctweight, dtweights_dt, wh, dat, dt, o)
   # if there is a "W" entry in dat.D then this means all defaults
   # are over-written
   if haskey(dat.info, "W")
      # now check that this observation type (E, F, V) exists in dat.D["W"]
      # if yes return the corresponding weight, if not make it zero
      # which means that we will simply ignore this observation
      if haskey(dat.info["W"], dt)
         w = dat.info["W"][dt]
      else
         w = 0.0
      end
   else
      # if no "W" dict exists use the default weights
      w = ctweight * dtweights_dt * wh
   end
   # transform the weights into a vector (if necessary) and return
   if length(w) == 1
      return w * ones(length(o))
   elseif length(w) == length(o)
      return w
   end
   error("_get_weights: length(w) is neither 1 nor length(o)?!?!?")
end

function _regularise!(Ψ::Matrix{T}, Y::Vector{T}, basis, regularisers;
                      verbose=false) where {T}
   # assemble the regularisers
   Ψreg = Matrix{Float64}(undef, 0, size(Ψ, 2))
   Yreg = Float64[]
   for reg in regularisers
      if reg isa Matrix
         P = reg
         q = zeros(size(P, 1))
      else
         P, q = Matrix(reg, basis; verbose=verbose)
      end
      Ψreg = vcat(Ψreg, P)
      Yreg = vcat(Yreg, q)
   end
   # check they all have the correct size
   return vcat(Ψ, Ψreg), vcat(Y, Yreg)
end

"""
`get_lsq_system(db::LsqDB; kwargs...) -> Ψ, Y`

Assemble the least squares system + rhs (observations). The `kwargs` can be used
to select a subset of the available data or basis set, and to adjust the
weights by config_type and observation type. See `lsqfit` for a list
of keyword arguments. Allowed kwargs are `verbose, configweights, obsweights,
E0, Ibasis`.
"""
@noinline function get_lsq_system(db::LsqDB;
                         solver=(:qr, ), verbose=true,
                         Ibasis = :, Itrain = :,
                         E0 = nothing,
                         Vref = OneBody(E0),
                         configweights = nothing,
                         obsweights = nothing,
                         regularisers = [],
                         kwargs...)

   # get the observations vector and the weights vector
   # the Vref potential is subtracted from the observations
   Y, W, Icfg = collect_observations(db, configweights, obsweights, Vref)

   # check for NaNs
   any(isnan, Y) && @error("NaN detected in observations vector")
   any(isnan, W) && @error("NaN detected in weights vector")

   # convert : into a vector or list
   if Itrain == : ; Itrain = 1:length(Y); end

   # get the right slice of the big fat huge enourmous LSQ matrix
   # the columns are just the basis functions
   Icols = Ibasis
   # the rows are those which have (a) a non-zero weight
   # and (b) the configuration index is part of the training set
   Irows = intersect(findall(W .!= 0) |> sort, findall(in.(Icfg, Ref(Itrain))))

   # we can't keep this a view since we need to multiply by weights
   Ψ = db.Ψ[Irows, Icols]
   Y = Y[Irows]
   W = W[Irows]

   if any(isnan, Ψ)
      @info("discovered NaNs in LSQ system matrix")

      naninds = findall(isnan.(Ψ))
      n = min(length(naninds), 10)
      @show length(naninds)
      @show naninds[1:n]

      @error("discovered NaNs in LSQ system matrix")
   end

   # now rescale Y and Ψ according to W => Y_W, Ψ_W; then the two systems
   #   \| Y_W - Ψ_W c \| -> min  and \| W (Y - Ψ*c)^T \| -> min
   # are equivalent
   @. Y = Y * W
   lmul!(Diagonal(W), Ψ)

   # regularise
   if regularisers != nothing
      Ψ, Y = _regularise!(Ψ, Y, db.basis[Ibasis], regularisers; verbose=verbose)
   end

   # this should be it ...
   return Ψ, Y
end

@noinline function onb(db::LsqDB;
                         solver=(:qr, ), verbose=true,
                         Ibasis = :,
                         Itrain = :,
                         E0 = nothing,
                         Vref = OneBody(E0),
                         configweights = nothing,
                         obsweights = nothing,
                         regularisers = [],
                         combineIP = nothing,
                         kwargs...)

   verbose && @info("assemble lsq system")
   Ψ, _ = get_lsq_system(db; verbose=verbose, Vref=Vref,
                             Ibasis=Ibasis, Itrain=Itrain,
                             configweights = configweights,
                             obsweights = obsweights,
                             regularisers = regularisers,
                             kwargs...)
   @assert solver[1] == :qr
   verbose && @info("QR-factorize Ψ, size=$(size(Ψ))")
   qrΨ = qr(Ψ)
   verbose && @info("cond(R) = $(cond(qrΨ.R))")
   Rinv = pinv(qrΨ.R)
   basis = db.basis[Ibasis]
   onb = []
   for n = 1:size(Rinv, 2)
      push!(onb, combineIP(basis, Rinv[:,n]))
   end
   return [b for b in onb]
end

"""
`lsqfit(db; kwargs...) -> IP, errs`

Given the pre-computed least-squares system `db` (cf `LsqDB`) setup a least
squares system, compute the solution and return an interatomic potential
`IP` as well as the associated error estiamates `errs`.

## Keyword Arguments:

* `E0` (required unless `Vref` is provided) : energy of the `OneBody` term
* `Vref` : a reference potential from which to start the fit.
* `configweights` (required) : A dictionary specifying the weights for different
types of configurations, e.g.,
```
configweights = Dict("solid" => 10.0, "liquid" => 0.1)
```
The keys, `["solid", "liquid"]` in the above example, specify which configtypes
are to be fitted - all other configtypes are ignored.
* `obsweights` (required) : a `Dict` specifying how different kinds of
observations should be weighted, e.g.,
```
obsweights = Dict("E" => 100.0, "F" => 1.0, "V" => 0.1)
```
* `Ibasis` : indices of basis functions to be used in the fit, default is `:`
* `verbose` : true or false
* `solver` : -experimental, still need to  write the docs for this-
* `regularisers` : a list of regularisers to be added to the lsq functional.
Each regulariser `R` can be of an arbtirary type but this type must implement the
conversion to matrix
* `combineIP` : this is a required kwarg, it tells `lsqfit` how to
combine `basis` and `coeffs` into an IP by calling `combineIP(basis, coeffs)`;
use `(b, c) -> c` to just return the coefficients.
```
Matrix(R, basis; verbose={true,false})
```
If `Areg = Matrix(R, basis)` then this corresponds to adding
`|| Areg * x ||²` to the least squares functional.

## More on Weights

The `configweights` and `obsweights` dictionaries specify weights as follows:
for an observation `o` from a config `cfg` where the `obsweights is `wo` and
the configweight is `wc` the weight on this observation `o` will be `w = w0*wc`.
This given a diagonal weight matrix `W`. The least squares functional then becomes
|| W (A * x - Y) ||²` where `x` are the unknown coefficients.

## Return types

* `IP`: whatever `combineIP` returns
* `errs::LsqErrors`: stores individual RMSE and MAE for the different
configtypes and datatypes. Use `table_relative(errs)` and `table_absolute(errs)`
to display these as tables and `rmse, mae` to access individual errors.
"""
@noinline function lsqfit(db::LsqDB;
                solver=(:qr, ), verbose=true,
                Ibasis = :,
                Itrain = :,
                Itest = nothing,
                E0 = nothing,
                Vref = OneBody(E0),
                configweights = nothing,
                obsweights = nothing,
                regularisers = [],
                combineIP = nothing,
                kwargs...)

   Jbasis = ((Ibasis == Colon()) ? (1:length(db.basis)) : Ibasis)

   verbose && @info("assemble lsq system")
   Ψ, Y = get_lsq_system(db; verbose=verbose, Vref=Vref, Ibasis=Ibasis, Itrain = Itrain,
                             configweights = configweights,
                             obsweights = obsweights,
                             regularisers = regularisers,
                             kwargs...)

   if (solver[1] == :qr) || (solver == :qr)
      verbose && @info("solve $(size(Ψ)) LSQ system using QR factorisation")
      qrΨ = qr(Ψ)
      verbose && @info("cond(R) = $(cond(qrΨ.R))")
      c = qrΨ \ Y

   elseif solver[1] == :svd
      verbose && @info("solve $(size(Ψ)) LSQ system using SVD factorisation")
      ndiscard = solver[2]
      F = svd(Ψ)
      c = F.V[:,1:(end-ndiscard)] * (Diagonal(F.S[1:(end-ndiscard)]) \ (F.U' * Y)[1:(end-ndiscard)])
   elseif solver[1] == :rrqr
       verbose && @info("solve $(size(Ψ)) LSQ system using Rank-Revealing QR factorisation")
       qrΨ = pqrfact(Ψ, rtol=solver[2])
       verbose && @info("cond(R) = $(cond(qrΨ.R))")
       c = qrΨ \ Y
   else
      error("unknown `solver` in `lsqfit`")
   end

   if verbose
      rel_rms = norm(Ψ * c - Y) / norm(Y)
      @info("Relative RMSE on training set: $rel_rms")
   end

   infodict = asm_fitinfo(db, c, Ibasis, configweights, obsweights,
                          Vref, solver, E0, regularisers, verbose,
                          Itrain, Itest)

   if Vref != nothing
      basis = [ Vref; db.basis[Ibasis] ]
      c = [1.0; c]
   else
      basis = db.basis[Ibasis]
   end

   return combineIP(basis, c), infodict
end

function asm_fitinfo(db, c, Ibasis, configweights, obsweights,
                     Vref, solver, E0, regularisers, verbose,
                     Itrain = :, Itest = nothing)
   if Ibasis isa Colon
      Jbasis = collect(1:length(db.basis))
   else
      Jbasis = Ibasis
   end
   # compute errors TODO: still need to fix this!
   verbose && @info("Assemble errors table")
   @warn("new error implementation... redo this part please ")
   if Vref == nothing
      Vref=OneBody(E0)
   end
   errs = Err.lsqerrors(db, c, Jbasis;
            cfgtypes=keys(configweights), Vref=Vref, Icfg=Itrain)
   if Itest != nothing
      errtest = Err.lsqerrors(db, c, Jbasis;
               cfgtypes=keys(configweights), Vref=Vref, Icfg=Itest)
   else
      errtest = Dict()
   end

   if Vref != nothing
      basis = [ Vref; db.basis[Jbasis] ]
      c = [1.0; c]
   else
      basis = db.basis[Jbasis]
   end

   # --------------------------------------------------------------------
   # ASSEMBLE INFO DICT
   # --------------------------------------------------------------------
   verbose && @info("Assemble Information about the fit")

   # Julia Version Info
   iob = IOBuffer()
   versioninfo(iob)
   juliainfo = String(take!(iob))

   infodict = Dict("errors" => errs,
                   "errtest" => errtest,
                   "solver" => String(solver[1]),
                   "E0"     => E0,
                   "Ibasis" => Vector{Int}(Jbasis),
                   "c"      => c,
                   "dbpath" => dbpath(db),
                   "configweights" => configweights,
                   "confignames"   => keys(configweights),
                   "obsweights"    => obsweights,
                   "regularisers"  => Dict.(regularisers),
                   "juliaversion"  => juliainfo,
                   "IPFitting_version" => get_pkg_info("IPFitting"),
                  )
   # --------------------------------------------------------------------
   return infodict
end

import Pkg

function get_pkg_info(pkg::AbstractString)
    pkgs = [Pkg.API.check_package_name(pkg)]
    ctx = Pkg.Types.Context()
    Pkg.API.project_resolve!(ctx.env, pkgs)
    Pkg.API.project_deps_resolve!(ctx.env, pkgs)
    Pkg.API.manifest_resolve!(ctx.env, pkgs)
    Pkg.API.ensure_resolved(ctx.env, pkgs)
    i = Pkg.Display.status(ctx, pkgs)[1]
    return Dict("name" => i.name,
                "uuid" => string(i.uuid),
                "ver" => string(i.new.ver))
end

end


text:
"""
```julia
    Poisson Likelihood(λ::T=1.0)
```
[Poisson Likelihood](https://en.wikipedia.org/wiki/Poisson_distribution) where a Poisson distribution is defined at every point in space (careful, it's different from continous Poisson processes)
```math
    p(y|f) = Poisson(y|λσ(f))
```
Where `σ` is the logistic function
Augmentation details will be released at some point (open an issue if you want to see them)
"""
mutable struct PoissonLikelihood{T<:Real} <: EventLikelihood{T}
    λ::T
    c::Vector{T}
    γ::Vector{T}
    θ::Vector{T}
    function PoissonLikelihood{T}(λ::T) where {T<:Real}
        new{T}(λ)
    end
    function PoissonLikelihood{T}(λ,c,γ,θ) where {T<:Real}
        new{T}(λ,c,γ,θ)
    end
end

function PoissonLikelihood(λ::T=1.0) where {T<:Real}
    PoissonLikelihood{T}(λ)
end

function init_likelihood(likelihood::PoissonLikelihood{T},inference::Inference{T},nLatent::Integer,nSamplesUsed::Int,nFeatures::Int) where T
    PoissonLikelihood{T}(
    likelihood.λ,
    zeros(T,nSamplesUsed),
    zeros(T,nSamplesUsed),
    zeros(T,nSamplesUsed))
end

function pdf(l::PoissonLikelihood,y::Real,f::Real)
    pdf(Poisson(get_p(l,l.λ,f)),y)
end

function expec_count(l::PoissonLikelihood,f)
    get_p(l,l.λ,f)
end

function get_p(::PoissonLikelihood,λ::Real,f)
    λ*logistic.(f)
end

function Base.show(io::IO,model::PoissonLikelihood{T}) where T
    print(io,"Poisson Likelihood")
end

function compute_proba(l::PoissonLikelihood{T},μ::Vector{T},σ²::Vector{T}) where {T<:Real}
    N = length(μ)
    pred = zeros(T,N)
    for i in 1:N
        x = pred_nodes.*sqrt.(max(σ²[i],zero(T))).+μ[i]
        pred[i] =  dot(pred_weights,get_p(l,l.λ,x))
    end
    return pred
end

## Local Updates ##

function local_updates!(l::PoissonLikelihood{T},y::AbstractVector,μ::AbstractVector,diag_cov::AbstractVector) where {T}
    l.c .= sqrt.(abs2.(μ) + diag_cov)
    l.γ .= 0.5*l.λ*safe_expcosh.(-0.5*μ,0.5*l.c)
    l.θ .= (y+l.γ)./l.c.*tanh.(0.5*l.c)
    l.λ = sum(y)./sum(expectation.(logistic,μ,diag_cov))
end

## Global Updates ##

@inline ∇E_μ(l::PoissonLikelihood,::AOptimizer,y::AbstractVector) = (0.5*(y-l.γ),)
@inline ∇E_Σ(l::PoissonLikelihood,::AOptimizer,y::AbstractVector) = (0.5*l.θ,)

## ELBO Section ##
function expec_log_likelihood(l::PoissonLikelihood{T},i::AnalyticVI,y,μ::AbstractVector,Σ::AbstractVector) where {T}
    tot = sum(y*log(l.λ))-sum(lfactorial,y)-logtwo*sum((y+l.γ))
    tot += 0.5*(dot(μ,(y-l.γ))-dot(l.θ,abs2.(μ))-dot(l.θ,Σ))
    return tot
end

AugmentedKL(l::PoissonLikelihood,y::AbstractVector) = PoissonKL(l)+PolyaGammaKL(l,y)

PoissonKL(l::PoissonLikelihood) = PoissonKL(l.γ,l.λ)

PolyaGammaKL(l::PoissonLikelihood,y::AbstractVector) = PolyaGammaKL(y+l.γ,l.c,l.θ)


text:
# Autogenerated wrapper script for LinuxKernelHeaders_jll for x86_64-linux-musl-target_os+linux-target_arch+x86_64
export limits_h

JLLWrappers.@generate_wrapper_header("LinuxKernelHeaders")
JLLWrappers.@declare_file_product(limits_h)
function __init__()
    JLLWrappers.@generate_init_header()
    JLLWrappers.@init_file_product(
        limits_h,
        "include/linux/limits.h",
    )

    JLLWrappers.@generate_init_footer()
end  # __init__()


text:
abstract type Modeltype
end

# mutable struct myFloat <:AbstractFloat
# end

mutable struct Parameters{AT,FT}
    K::AT
    specificStorage::FT
    # Δt::Float64
end

mutable struct BoundaryConditions{AT}
    bcPressure::AT
end

mutable struct Physics{AT,FT}
    g::FT
    nuₕ::AT
    nuᵥ::AT
end

mutable struct ModelComponents
    # advection::Bool
    # diffusion::Bool
    # bottom_friction::Bool
    # wind_friction::Bool
    # gravity::Bool
    hydraulicConductivity::Bool
    massFlux::Bool
    pressure::Bool
    sources::Bool
end

mutable struct State{AT}
    h::AT
    u::AT
    v::AT
    w::AT
    hⁿ⁺¹::AT
    uⁿ⁺¹::AT
    vⁿ⁺¹::AT
    wⁿ⁺¹::AT
end

mutable struct Source{AT,FT}
    i_src::Int64
    j_src::Int64
    k_src::Int64
    duration::FT
    discharge::FT
    externalSource::AT
end

mutable struct Recharge{FT}
    # duration::AbstractFloat
    constRecharge::FT
    rechargeFlux::FT
    rechargeFactor::FT
end

mutable struct Model
    source::Source
    recharge::Recharge
    boundaryConditions::BoundaryConditions
end

mutable struct TimeData{FT}
    Δt::FT
    tend::FT
    time::FT
    maxsteps::Int64
end

mutable struct SolverData{AT,FT}
    ΔhConv::FT
    Δh::AT
end

mutable struct Simulation
    grid
    model
    state
    parameters
    timeData
    solverData
end


text:
# Exceptions

struct UnimplementedError <: Exception
    func::Symbol
    type::Type
    UnimplementedError(func::Symbol, any) = new(func, typeof(any))
end

Base.showerror(io::IO, e::UnimplementedError) = print(io, e.type, " doesn't implement ", e.func)

max_stars_repo_name: jrdegreeff/cesmix-julia

max_issues_repo_name: jrdegreeff/cesmix-julia

max_forks_repo_name: jrdegreeff/cesmix-julia


text:
function psracos_opt!(objective::Objective, parameter::Parameter)
    psracos = PSRacos(parameter.evaluation_server_num)
    rc = psracos.rc
    rc.objective = objective
    rc.parameter = parameter
    ub = isnull(parameter.uncertain_bits)? 1 : parameter.uncertain_bits

    # require calculator server
    ip = parameter.control_server_ip
    port = parameter.control_server_port
    cs_send = connect(ip, port)
    println(cs_send, "client: require servers#")
    readline(cs_send)
    println(cs_send, string(parameter.evaluation_server_num, "#"))
    msg = readline(cs_send)

    servers_msg = readline(cs_send)
    if servers_msg == "No available evaluation server"
        zoolog("Error: no available evaluation server")
        return Solution()
    end
    servers = split(servers_msg, " ")
    println("get $(length(servers)) servers")
    # close the socket
    close(cs_send)
    parameter.ip_port = RemoteChannel(()->Channel(length(servers)))
    for server in servers
        put!(parameter.ip_port, server)
    end
    psracos_init_attribute!(psracos, parameter)
    psracos_init_sample_set!(psracos, ub)
    println("Initialization succeeds")
    history = []
    # addprocs(1)
    i = parameter.train_size
    output_file = parameter.output_file
    f = Nullable()
    if !isnull(output_file)
        f = open(output_file, "w")
    end
    time_log1 = now()
    while i < parameter.budget
        # parallel estimation
        iteration_num = min(parameter.evaluation_server_num, parameter.budget-i)
        for j = 1:iteration_num
            ip_port = take!(parameter.ip_port)
            sol = take!(psracos.sample_set)
            @spawn begin
                try
                    br = compute_fx(sol, ip_port, parameter)
                    put!(parameter.ip_port, ip_port)
                    put!(psracos.result_set, sol)
                catch e
                    println("Exception")
                    println(e)
                    cs_exception = connect(ip, port)
                    println(cs_exception, "client: restart#")
                    readline(cs_exception)
                    println(cs_exception, string(servers_msg, "#"))
                    close(cs_exception)
                    return Solution()
                end
            end
        end
        # sequential update
        sol = Nullable()
        for j=1:iteration_num
            sol = take!(psracos.result_set)
            push!(history, sol.value)
            bad_ele = sracos_replace!(rc.positive_data, sol, "pos")
            sracos_replace!(rc.negative_data, bad_ele, "neg", strategy=parameter.replace_strategy)
            rc.best_solution = rc.positive_data[1]

    		 if rand(rng, Float64) < rc.parameter.probability
    			 classifier = RacosClassification(rc.objective.dim, rc.positive_data,
    			 rc.negative_data, ub=ub)
    			 mixed_classification(classifier)
    			 solution, distinct_flag = distinct_sample_classifier(rc, classifier, data_num=rc.parameter.train_size)
    		 else
    			 solution, distinct_flag = distinct_sample(rc, rc.objective.dim)
    		 end
    		 if distinct_flag == false
    			 zoolog("ERROR: dimension limited")
    			 break
    		 end
    		 if isnull(solution)
    			 zoolog("ERROR: solution null")
    			 break
    		 end
    		 put!(psracos.sample_set, solution)
    		 i += 1
        end
        time_log2 = now()
        time_pass = Dates.value(time_log2 - time_log1) / 1000
        zoolog("Budget $(i): time=$(floor(time_pass))s, value=$(sol.value), best_solution_value=$(rc.best_solution.value)")
        str = "Budget $(i): time=$(floor(time_pass))s, value=$(sol.value), best_solution_value=$(rc.best_solution.value)\nbest_x=$(rc.best_solution.x)\n"
        if !isnull(f)
            write(f, str)
            flush(f)
        end
        if !isnull(parameter.time_limit) && time_pass > parameter.time_limit
            zoolog("Exceed time limit: $(parameter.time_limit)")
            break
        end
    end
    # finish task
    result = rc.best_solution
    objective.history = history
    parameter.positive_data = rc.positive_data
    parameter.negative_data = rc.negative_data
    cs_receive = connect(ip, port)
    println(cs_receive, "client: return servers#")
    readline(cs_receive)
    println(cs_receive, string(servers_msg, "#"))
    close(cs_receive)
    return result
end

# function psracos_sample(rc, ub)
#     if rand(rng, Float64) < rc.parameter.probability
#         classifier = RacosClassification(rc.objective.dim, rc.positive_data,
#         rc.negative_data, ub=ub)
#         zoolog("before classification")
#         mixed_classification(classifier)
#         zoolog("after classification")
#         solution, distinct_flag = distinct_sample_classifier(rc, classifier, data_num=rc.parameter.train_size)
#     else
#         solution, distinct_flag = distinct_sample(rc, rc.objective.dim)
#     end
#     #painc stop
#     if distinct_flag == false
#         zoolog("ERROR: dimension limited")
#     end
#     if isnull(solution)
#         zoolog("ERROR: solution null")
#     end
#     return solution
# end

function psracos_init_attribute!(psracos::PSRacos, parameter::Parameter)
    f = open("init.txt", "w")
    rc = psracos.rc
    # otherwise generate random solutions
    iteration_num = rc.parameter.train_size
    data_temp = rc.parameter.init_sample
    init_num = 0
    remote_data = RemoteChannel(()->Channel(iteration_num))
    remote_result = RemoteChannel(()->Channel(iteration_num))
    if !isnull(data_temp)
        init_num = length(data_temp) < iteration_num? length(data_temp):iteration_num
        for i = 1:init_num
            # str = "initial sample: $(i), value=$(data_temp[i].value)\nx=$(sol.x)\n"
            put!(remote_data, data_temp[i])
        end
    end
    i = 1
    while i <= iteration_num - init_num
        # distinct_flag: True means sample is distinct(can be use),
        # False means sample is distinct, you should sample again.
        sol, distinct_flag = distinct_sample_from_set(rc, rc.objective.dim, rc.data,
            data_num=iteration_num)
        # panic stop
        if isnull(sol)
            break
        end
        if distinct_flag
            put!(remote_data, sol)
            i += 1
        end
    end
    fn = RemoteChannel(()->Channel(1))
    for i = 1:iteration_num
        d = take!(remote_data)
        if d.value != 0
            put!(remote_result, d)
            if i == iteration_num
                put!(fn, 1)
            end
            continue
        end
        ip_port = take!(parameter.ip_port)
        @spawn begin
            compute_fx(d, ip_port, parameter)
            put!(parameter.ip_port, ip_port)
            put!(remote_result, d)
            if i == iteration_num
                put!(fn, 1)
            end
        end

    end
    result = take!(fn)
    f = open("init.txt", "w")
    for i = 1:iteration_num
        d = take!(remote_result)
        push!(rc.data, d)
        str_print = "init sample: $(i), value=$(d.value)"
        str = "init sample: $(i), value=$(d.value)\n x=$(d.x)\n"
        zoolog(str_print)
        write(f, str)
        flush(f)
    end
    # zoolog("after taking result")
    close(f)
    selection!(rc)
    return
end


text:
# Autogenerated wrapper script for Zlib_jll for i686-w64-mingw32
export libz

JLLWrappers.@generate_wrapper_header("Zlib")
JLLWrappers.@declare_library_product(libz, "libz.dll")
function __init__()
    JLLWrappers.@generate_init_header()
    JLLWrappers.@init_library_product(
        libz,
        "bin\\libz.dll",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@generate_init_footer()
end  # __init__()


text:
function dilate!( mask::Array{T,N}, dil::Array{Bool,N}, rad; kern=nothing, th=nothing ) where {T,N}

    if kern == nothing
        size = ones( Int64, N ) .* rad;
        kern = ones( UInt8, size... )
    end

    conv = FFTConvolution_crop( mask, kern, typ=Float32 );

    th = ( th == nothing ) ? 1 - 0.1 : th;

    @inbounds @simd for e in 1:length(conv)
        dil[e] = conv[e] > th;
    end

    return dil
end

function dilate( mask::Array{T,N}, rad; kern=nothing, th=nothing ) where {T,N}

    return dilate!( mask, zeros(Bool,size(mask)), rad, kern=kern, th=th );
end

function erode!( mask::Array{T,N}, ero::Array{Bool,N}, rad; kern=nothing, th=nothing ) where {T,N}

    if kern == nothing
        kern = ones( UInt8, (ones( Int64, N ) .*  rad)... ) # ones( rad, rad ) or ones( rad, rad, rad )
    end

    th = ( th == nothing ) ? sum( kern ) - 0.1 : th;

    conv = FFTConvolution_crop( mask, kern, typ=Float32 );

    for e in 1:length(conv)
        ero[e] = ( conv[e] > th )
    end

    return ero

end

function erode( mask::Array{T,N}, rad; kern=nothing, th=nothing ) where {T,N}

    return erode!( mask, zeros(Bool,size(mask)), rad, kern=kern, th=th );
end

function closing( mask, size; kern=nothing )
    s1 = dilate( mask, size, kern=kern  )
    s2 = erode( s1, size, kern=kern )
    return s2
end

function opening( mask, size; kern=nothing )
    s1 = erode( mask, size, kern=kern )
    s2 = dilate( s1, size, kern=kern )
    return s2
end

function distanceTransform( mask )

    distances = zeros( UInt16, size(mask) )

    kern_size = 3;
    keepgoing = true
    while keepgoing

        kern  = ones( UInt8, kern_size, kern_size );
        th    = sum( kern );

        mask_ = convolution( UInt8.( mask ), kern );
        h, w  = size( mask_ );
        off   = div( kern_size, 2 );

        trues = 0;
        for col in off:w-off
            for row in off:h-off
                if mask_[row,col] >= length(kern) - 0.1
                    distances[ row-off+1, col-off+1 ] += 1;
                    trues += 1;
                end
            end
        end

        if trues == 0
            keepgoing = false;
        end

        kern_size += 2;
    end

    println( "maximum kern_size: ", kern_size )

    return distances
end

max_stars_repo_name: BrucePBon/ImageAnalysis

max_issues_repo_name: BrucePBon/ImageAnalysis

max_forks_repo_name: BrucePBon/ImageAnalysis


text:
importall Base

# figure 5.2 from principles of parallel programming, ported to julia.
# sum a vector using a tree on top of local reductions.
function sum(v::DArray)
    P = procs(v)
    nodeval = { RemoteRef(p) for p=P }
    answer = RemoteRef()
    np = numel(P)

    for i=0:np-1
        @spawnat P[i+1] begin
            stride=1
            tally = sum(localpart(v))
            while stride < np
                if i%(2*stride) == 0
                    tally = tally + take(nodeval[i+stride])
                    stride = 2*stride
                else
                    put(nodeval[i], tally)
                    break
                end
            end
            if i==0
                put(answer, tally)
            end
        end
    end
    fetch(answer)
end

function reduce(f, v::DArray)
    mapreduce(fetch, f,
              { @spawnat p reduce(f,localpart(v)) for p = procs(v) })
end

# possibly-useful abstraction:

type RefGroup
    refs::Array{RemoteRef,1}

    RefGroup(P) = new([ RemoteRef(p) for p=P ])
end

getindex(r::RefGroup, i) = fetch(r.refs[i])
setindex!(r::RefGroup, v, i) = put(r.refs[i], v)

max_stars_repo_name: jakebolewski/julia

max_issues_repo_name: jakebolewski/julia

max_forks_repo_name: jakebolewski/julia


text:
# Dict -> GeoJSON

function dict2geojson(obj::Dict)
    t = Symbol(obj["type"])
    if t == :FeatureCollection
        return FeatureCollection(obj)
    elseif t == :Feature
        return Feature(obj)
    elseif t == :GeometryCollection
        return GeometryCollection(obj)
    elseif t == :MultiPolygon
        return MultiPolygon(obj)
    elseif t == :Polygon
        return Polygon(obj)
    elseif t == :MultiLineString
        return MultiLineString(obj)
    elseif t == :LineString
        return LineString(obj)
    elseif t == :MultiPoint
        return MultiPoint(obj)
    elseif t == :Point
        return Point(obj)
    end
end

dict2geojson(obj::Void) = obj

# GeoJSON -> Dict

for geom in (MultiPolygon, MultiLineString, MultiPoint,
             Polygon, LineString, Point)
    @eval begin
        function geojson2dict(obj::$geom)
            dict = Dict("type" => string($(geom).name.name),
                                  "coordinates" => coordinates(obj))
            hasbbox(obj) && (dict["bbox"] = bbox(obj))
            hascrs(obj) && (dict["crs"] = crs(obj))
            dict
        end
    end
end

function geojson2dict(obj::GeometryCollection)
    dict = Dict("type" => "GeometryCollection",
                          "geometries" => map(geojson2dict, geometries(obj)))
    hasbbox(obj) && (dict["bbox"] = bbox(obj))
    hascrs(obj) && (dict["crs"] = crs(obj))
    dict
end

function geojson2dict(obj::Feature)
    dict = Dict("type" => "Feature",
                          "properties" => properties(obj),
                          "geometry" => geojson2dict(geometry(obj)))
    hasbbox(obj) && (dict["bbox"] = bbox(obj))
    hascrs(obj) && (dict["crs"] = crs(obj))
    hasid(obj) && (dict["id"] = id(obj))
    dict
end

function geojson2dict(obj::FeatureCollection)
    dict = Dict("type" => "FeatureCollection",
                          "features" => map(geojson2dict, features(obj)))
    hasbbox(obj) && (dict["bbox"] = bbox(obj))
    hascrs(obj) && (dict["crs"] = crs(obj))
    dict
end

geojson2dict(obj::Void) = obj

# String/File -> GeoJSON
parse(input; kwargs...) = dict2geojson(JSON.parse(input; kwargs...))
parsefile(filename; kwargs...) = dict2geojson(JSON.parsefile(filename; kwargs...))

# GeoJSON -> String/IO
geojson(obj::AbstractGeoJSON) = JSON.json(geojson2dict(obj))
print(io::IO, obj::AbstractGeoJSON) = JSON.print(io, geojson2dict(obj))


text:
## Options preprocessing

function normalize_link(link, var, consistent, directionally_consistent)
    directionwise = var == :x ? :colwise : :rowwise
    default = consistent ? :all : directionally_consistent ? directionwise : :none
    link === automatic && return default
    link == :minimal && return directionally_consistent ? directionwise : :none
    link == true && return :all
    link == false && return :none
    link in (:all, directionwise, :none) && return link
    attribute = Symbol(:link, var, :axes)
    @warn "Replaced invalid keyword $attribute = $(repr(link)) by automatic. " *
        "Valid values are :all, $(repr(directionwise)), :minimal, :none, true, false, or automatic."
    return default
end

function normalize_hide(hide, link, var)
    default = link != :none
    hide === automatic && return default
    hide isa Bool && return hide
    attribute = Symbol(:hide, var, :decorations)
    @warn "Replaced invalid keyword $attribute = $(repr(hide)) by automatic. " *
        "Valid values are true, false, or automatic."
    return default
end

function clean_facet_attributes(aes;
                                linkxaxes=automatic, linkyaxes=automatic,
                                hidexdecorations=automatic, hideydecorations=automatic)
    linkxaxes = normalize_link(linkxaxes, :x, consistent_xlabels(aes), colwise_consistent_xlabels(aes))
    linkyaxes = normalize_link(linkyaxes, :y, consistent_ylabels(aes), rowwise_consistent_ylabels(aes))
    hidexdecorations = normalize_hide(hidexdecorations, linkxaxes, :x)
    hideydecorations = normalize_hide(hideydecorations, linkyaxes, :y)
    return (; linkxaxes, linkyaxes, hidexdecorations, hideydecorations)
end

## Label computation and layout

# facet labels
function facet_labels!(fig, aes, scale, dir)
    # reference axis to extract attributes
    ax = first(nonemptyaxes(aes))
    color = ax.titlecolor
    font = ax.titlefont
    textsize = ax.titlesize
    visible = ax.titlevisible

    padding_index = dir == :row ? 1 : 3
    padding = lift(ax.titlegap) do gap
        return ntuple(i -> i == padding_index ? gap : 0f0, 4)
    end

    return map(plotvalues(scale), datavalues(scale)) do index, label
        rotation = dir == :row ? -π/2 : 0.0
        figpos = dir == :col ? fig[1, index, Top()] :
                 dir == :row ? fig[index, size(aes, 2), Right()] : fig[index..., Top()]
        return Label(figpos, to_string(label); rotation, padding, color, font, textsize, visible)
    end
end

col_labels!(fig, aes, scale) = facet_labels!(fig, aes, scale, :col)
row_labels!(fig, aes, scale) = facet_labels!(fig, aes, scale, :row)
panel_labels!(fig, aes, scale) = facet_labels!(fig, aes, scale, :layout)

# Consistent axis labels

function consistent_attribute(aes, attr)
    axes = nonemptyaxes(aes)
    ax = first(axes)
    return all(axis -> getproperty(axis, attr)[] == getproperty(ax, attr)[], axes)
end

consistent_xlabels(aes) = consistent_attribute(aes, :xlabel)
consistent_ylabels(aes) = consistent_attribute(aes, :ylabel)

colwise_consistent_xlabels(aes) = all(consistent_xlabels, eachcol(aes))
rowwise_consistent_ylabels(aes) =  all(consistent_ylabels, eachrow(aes))

# spanned axis labels
function span_label!(fig, aes, var)
    getattr = GetAttr(var)
    for ae in aes
        getattr(ae.axis, :labelvisible)[] = false
    end

    ax = first(nonemptyaxes(aes))

    pos = var == :x ? (size(aes, 1), :) : (:, 1)
    index, side, Side = var == :x ? (4, :bottom, Bottom) : (2, :left, Left)

    protrusions = (protrusionsobservable(ae.axis) for ae in aes[pos...])
    padding = lift(getattr(ax, :labelpadding), protrusions...) do p, xs...
        protrusion = maximum(x -> getproperty(x, side), xs)
        return ntuple(i -> i == index ? protrusion + p : 0f0, 4)
    end

    label = getattr(ax, :label)
    rotation = var == :x ? 0.0 : π/2
    color = getattr(ax, :labelcolor)
    font = getattr(ax, :labelfont)
    textsize = getattr(ax, :labelsize)
    return Label(fig[pos..., Side()], label; rotation, padding, color, font, textsize)
end

span_xlabel!(fig, aes) = span_label!(fig, aes, :x)
span_ylabel!(fig, aes) = span_label!(fig, aes, :y)

## Apply default faceting look to a grid of `AxisEntries`

function facet_wrap!(fig, aes::AbstractMatrix{AxisEntries}; facet)

    scale = get(aes[1].categoricalscales, :layout, nothing)
    isnothing(scale) && return

    # Link axes and hide decorations if appropriate
    attrs = clean_facet_attributes(aes; facet...)
    link_axes!(aes; attrs.linkxaxes, attrs.linkyaxes)
    hideinnerdecorations!(aes; attrs.hidexdecorations, attrs.hideydecorations, wrap=true)

    # delete empty axes
    deleteemptyaxes!(aes)

    # add facet labels
    panel_labels!(fig, aes, scale)

    # span axis labels if appropriate
    is2d = all(isaxis2d, nonemptyaxes(aes))

    if is2d && consistent_ylabels(aes)
        span_ylabel!(fig, aes)
    end
    if is2d && consistent_xlabels(aes)
        span_xlabel!(fig, aes)
    end

    return
end

function facet_grid!(fig, aes::AbstractMatrix{AxisEntries}; facet)
    row_scale, col_scale = map(sym -> get(aes[1].categoricalscales, sym, nothing), (:row, :col))
    all(isnothing, (row_scale, col_scale)) && return

    # link axes and hide decorations if appropriate
    attrs = clean_facet_attributes(aes; facet...)
    link_axes!(aes; attrs.linkxaxes, attrs.linkyaxes)
    hideinnerdecorations!(aes; attrs.hidexdecorations, attrs.hideydecorations, wrap=false)

    # span axis labels if appropriate
    is2d = all(isaxis2d, nonemptyaxes(aes))

    if !isnothing(row_scale) && consistent_ylabels(aes)
        is2d && span_ylabel!(fig, aes)
        row_labels!(fig, aes, row_scale)
    end
    if !isnothing(col_scale) && consistent_xlabels(aes)
        is2d && span_xlabel!(fig, aes)
        col_labels!(fig, aes, col_scale)
    end
    return
end

function facet!(fig, aes::AbstractMatrix{AxisEntries}; facet)
    update(fig) do f
        facet_wrap!(f, aes; facet)
        facet_grid!(f, aes; facet)
    end
    return
end

function facet!(fg::FigureGrid; facet)
    facet!(fg.figure, fg.grid; facet)
    return fg
end

## Layout helpers

isaxis2d(::Axis) = true
isaxis2d(::Axis3) = false
isaxis2d(ax::AxisSpec) = ax.type <: Axis
isaxis2d(ae::AxisEntries) = isaxis2d(ae.axis)
isaxis2d(ae::AxisSpecEntries) = isaxis2d(ae.axis)

for sym in [:hidexdecorations!, :hideydecorations!, :hidedecorations!]
    @eval function $sym(ae::AxisEntries; kwargs...)
        axis = ae.axis
        isaxis2d(axis) && $sym(axis; kwargs...)
    end
end

for sym in [:linkxaxes!, :linkyaxes!, :linkaxes!]
    @eval function $sym(ae::AxisEntries, aes::AxisEntries...)
        axs = filter(isaxis2d, map(ae->ae.axis, (ae, aes...)))
        isempty(axs) || $sym(axs...)
    end
end

# Handy non-splatting versions

link_xaxes!(aes::AbstractArray{AxisEntries}) = (linkxaxes!(aes...); aes)
link_yaxes!(aes::AbstractArray{AxisEntries}) = (linkyaxes!(aes...); aes)

function link_axes!(aes::AbstractArray{AxisEntries}; linkxaxes, linkyaxes)
    linkxaxes == :all && link_xaxes!(aes)
    linkxaxes == :colwise && foreach(link_xaxes!, eachcol(aes))

    linkyaxes == :all && link_yaxes!(aes)
    linkyaxes == :rowwise && foreach(link_yaxes!, eachrow(aes))

    return aes
end

# Handy grid-preserving versions

hide_xdecorations!(ax) = hidexdecorations!(ax; grid=false, minorgrid=false)
hide_ydecorations!(ax) = hideydecorations!(ax; grid=false, minorgrid=false)

function hideinnerdecorations!(aes::AbstractArray{AxisEntries};
                               hidexdecorations, hideydecorations, wrap)
    I, J = size(aes)

    if hideydecorations
        for i in 1:I, j in 2:J
            hide_ydecorations!(aes[i,j])
        end
    end

    if hidexdecorations
        for i in 1:I-1, j in 1:J
            if wrap && isempty(aes[i+1,j].entries)
                # In facet_wrap, don't hide x decorations if axis below is empty,
                # but instead improve alignment.
                aes[i,j].axis.alignmode = Mixed(bottom=Protrusion(0))
            else
                hide_xdecorations!(aes[i,j])
            end
        end
    end
end

# Miscellaneous utilities

struct GetAttr
    var::Symbol
end

(g::GetAttr)(collection, args...) = getproperty(collection, Symbol(g.var, args...))

nonemptyaxes(aes) = (ae.axis for ae in aes if !isempty(ae.entries))

function deleteemptyaxes!(aes::AbstractArray{AxisEntries})
    for ae in aes
        if isempty(ae.entries)
            delete!(ae.axis)
        end
    end
end

Makie.resize_to_layout!(fg::FigureGrid) = resize_to_layout!(fg.figure)

@deprecate resizetocontent!(fig) (resize_to_layout!(fig); fig)


text:
# Input data
data = BitArray(
    hcat(
        map(
            line -> map(x -> parse(Int64, x), split(line, "")),
            readlines("Day03.input"),
        )...,
    ),
);

# Part 1
γ = (A) -> (sum(A, dims = 2) .>= size(A, 2) / 2)';
ε = (A) -> γ(A) .⊻ 1;
todecimal = (V) -> sum(map(((i, v),) -> v << (size(V, 2) - i), enumerate(V)));

ans = todecimal(γ(data)) * todecimal(ε(data));
println("Part 1: $ans")

# Part 2
rating =
    (F, A) ->
        reduce(
            (acc, v) -> size(acc, 2) > 1 ? acc[:, findall(==(F(acc)[v]), acc[v, :])] : acc,
            1:size(A, 1),
            init = A,
        )';

ans = todecimal(rating(γ, data)) * todecimal(rating(ε, data));
println("Part 2: $ans")


text:
const BACKSPACE          = UInt8('\b')
const TAB                = UInt8('\t')
const NEWLINE            = UInt8('\n')
const FORM               = UInt8('\f')
const RETURN             = UInt8('\r')
const SPACE              = UInt8(' ')
const QUOTE              = UInt8('"')
const PLUS               = UInt8('+')
const COMMA              = UInt8(',')
const MINUS              = UInt8('-')
const PERIOD             = UInt8('.')
const FORWARDSLASH       = UInt8('/')
const BACKSLASH          = UInt8('\\')
const ZERO               = UInt8('0')
const NEG_ONE            = UInt8('0') - 0x01
const TEN                = UInt8('9') + 0x01
const BIG_A              = UInt8('A')
const BIG_E              = UInt8('E')
const BIG_F              = UInt8('F')
const BIG_I              = UInt8('I')
const BIG_N              = UInt8('N')
const BIG_T              = UInt8('T')
const BIG_Y              = UInt8('Y')
const LITTLE_A           = UInt8('a')
const LITTLE_B           = UInt8('b')
const LITTLE_E           = UInt8('e')
const LITTLE_F           = UInt8('f')
const LITTLE_I           = UInt8('i')
const LITTLE_L           = UInt8('l')
const LITTLE_N           = UInt8('n')
const LITTLE_R           = UInt8('r')
const LITTLE_S           = UInt8('s')
const LITTLE_T           = UInt8('t')
const LITTLE_U           = UInt8('u')
const LITTLE_Y           = UInt8('y')
const OPEN_CURLY_BRACE   = UInt8('{')
const CLOSE_CURLY_BRACE  = UInt8('}')
const COLON              = UInt8(':')
const OPEN_SQUARE_BRACE  = UInt8('[')
const CLOSE_SQUARE_BRACE = UInt8(']')

function escapechar(b)
    b == QUOTE     && return QUOTE
    b == BACKSLASH && return BACKSLASH
    b == BACKSPACE && return LITTLE_B
    b == FORM      && return LITTLE_F
    b == NEWLINE   && return LITTLE_N
    b == RETURN    && return LITTLE_R
    b == TAB       && return LITTLE_T
    return 0x00
end

function reverseescapechar(b)
    b == QUOTE     && return QUOTE
    b == BACKSLASH && return BACKSLASH
    b == LITTLE_B  && return BACKSPACE
    b == LITTLE_F  && return FORM
    b == LITTLE_N  && return NEWLINE
    b == LITTLE_R  && return RETURN
    b == LITTLE_T  && return TAB
    return 0x00
end

charvalue(b) = (NEG_ONE < b < TEN)         ? b - ZERO            :
               (LITTLE_A <= b <= LITTLE_F) ? b - (LITTLE_A - 0x0a) :
               (BIG_A <= b <= BIG_F)       ? b - (BIG_A - 0x0a)    :
               throw(ArgumentError("JSON invalid unicode hex value"))

iscntrl(c::Char) = c <= '\x1f' || '\x7f' <= c <= '\u9f'
function escaped(b)
    if b == FORWARDSLASH
        return [FORWARDSLASH]
    elseif b >= 0x80
        return [b]
    elseif b in map(UInt8, ('"', '\\', '\b', '\f', '\n', '\r', '\t'))
        return [BACKSLASH, escapechar(b)]
    elseif iscntrl(Char(b))
        return UInt8[BACKSLASH, LITTLE_U, string(b, base=16, pad=4)...]
    else
        return [b]
    end
end

const ESCAPECHARS = [escaped(b) for b = 0x00:0xff]
const NEEDESCAPE = [length(x) > 1 for x in ESCAPECHARS]

function needescape(str)
    bytes = codeunits(str)
    @simd for i = 1:length(bytes)
        @inbounds NEEDESCAPE[bytes[i] + 0x01] && return true
    end
    return false
end

function write(io::IO, obj::AbstractString; kwargs...)
    Base.write(io, '"')
    if needescape(obj)
        bytes = codeunits(obj)
        for i = 1:length(bytes)
            @inbounds b = ESCAPECHARS[bytes[i] + 0x01]
            Base.write(io, b)
        end
    else
        Base.write(io, obj)
    end
    Base.write(io, '"')
    return
end

# read
const BACKSLASH = UInt8('\\')

@noinline invalid_escape(str) = throw(ArgumentError("encountered invalid escape character in json string: \"$str\""))
@noinline unescaped_control(b) = throw(ArgumentError("encountered unescaped control character in json: '$(Char(b))'"))

function unescape(s)
    n = ncodeunits(s)
    buf = Base.StringVector(n)
    len = 1
    i = 1
    @inbounds begin
        while i <= n
            b = codeunit(s, i)
            if b == BACKSLASH
                i += 1
                i > n && invalid_escape(s)
                b = codeunit(s, i)
                if b == LITTLE_U
                    c = 0x0000
                    i += 1
                    i > n && invalid_escape(s)
                    b = codeunit(s, i)
                    c = (c << 4) + charvalue(b)
                    i += 1
                    i > n && invalid_escape(s)
                    b = codeunit(s, i)
                    c = (c << 4) + charvalue(b)
                    i += 1
                    i > n && invalid_escape(s)
                    b = codeunit(s, i)
                    c = (c << 4) + charvalue(b)
                    i += 1
                    i > n && invalid_escape(s)
                    b = codeunit(s, i)
                    c = (c << 4) + charvalue(b)
                    st = codeunits(string(Char(c)))
                    for j = 1:length(st)-1
                        @inbounds buf[len] = st[j]
                        len += 1
                    end
                    b = st[end]
                else
                    b = reverseescapechar(b)
                    b == 0x00 && invalid_escape(s)
                end
            elseif b < SPACE
                unescaped_control(b)
            end
            @inbounds buf[len] = b
            len += 1
            i += 1
        end
    end
    resize!(buf, len - 1)
    return String(buf)
end

function read(io::IO, T::Type{String}; kwargs...)
    @expect '"'
    b = readbyte(io)
    hasescapechars = false
    while b != UInt8('"')
        Base.write(BUF, b)
        if b == UInt8('\\')
            eof(io) && throw(ArgumentError("early EOF"))
            hasescapechars = true
            b = readbyte(io)
            Base.write(BUF, b)
        end
        eof(io) && throw(ArgumentError("early EOF"))
        b = readbyte(io)
    end
    str = String(take!(BUF))
    return hasescapechars ? unescape(str) : str
end

function read(io::IOBuffer, T::Type{String}; kwargs...)
    @expect '"'
    ptr = pointer(io.data, io.ptr)
    b = readbyte(io)
    len = 0
    hasescapechars = false
    while b != QUOTE
        len += 1
        if b == BACKSLASH
            eof(io) && throw(ArgumentError("early EOF"))
            hasescapechars = true
            b = readbyte(io)
            len += 1
        end
        eof(io) && throw(ArgumentError("early EOF"))
        b = readbyte(io)
    end
    str = unsafe_string(ptr, len)
    return hasescapechars ? unescape(str) : str
end

function read(io::IOBuffer, T::Type{Symbol}; kwargs...)
    @expect '"'
    ptr = pointer(io.data, io.ptr)
    b = readbyte(io)
    len = 0
    hasescapechars = false
    while b != QUOTE
        len += 1
        if b == BACKSLASH
            eof(io) && throw(ArgumentError("early EOF"))
            hasescapechars = true
            b = readbyte(io)
            len += 1
        end
        eof(io) && throw(ArgumentError("early EOF"))
        b = readbyte(io)
    end
    sym = ccall(:jl_symbol_n, Ref{Symbol}, (Ptr{UInt8}, Int), ptr, len)
    return hasescapechars ? Symbol(unescape(unsafe_string(ptr, len))) : sym
end
# slow path
read(io::IO, ::Type{Symbol}; kwargs...) = Symbol(read(io, String; kwargs...))


text:
struct ImportContext{T₁ <: AbstractDialogControl,   T₂ <: AbstractDialogModel,  T₃ <: AbstractDisplayProperties, T₄ <: Union{AbstractImporter}} <: AbstractContext
    control::T₁
    model::T₂
    display_properties::T₃
    action::T₄
end

function (context::ImportContext)()
        control = context.control
        model = context.model
        display_properties = context.display_properties
        action = context.action
        isenabled(control) ? control(model, display_properties, action) : nothing
        data = isenabled(action) ? action(get_path(model, ConfirmedStatus())) : nothing
end

function isrunning(context::ImportContext)
        isenabled(context.control) || isenabled(context.action)
end

function enable!(context::ImportContext)
    enable!(context.control)
end

function disable!(context::ImportContext)
    disable!(context.control)
end


text:
import Base: @get!

include("read.jl")
include("dump.jl")
include("sugar.jl")

# Display

syntax(v::Vertex) = syntax(dl(v))

function Base.show(io::IO, v::Vertex)
  print(io, typeof(v))
  print(io, "(")
  s = MacroTools.alias_gensyms(syntax(v))
  if length(s.args) == 1
    print(io, sprint(print, s.args[1]))
  else
    foreach(x -> (println(io); print(io, sprint(print, x))), s.args)
  end
  print(io, ")")
end

import Juno: Row, Tree

code(x) = Juno.Model(Dict(:type=>"code",:text=>x,:attrs=>Dict(:block=>true)))

@render Juno.Inline v::Vertex begin
  s = MacroTools.alias_gensyms(syntax(v))
  Juno.LazyTree(typeof(v), () -> map(s -> code(string(s)), s.args))
end

# Function / expression macros

export @flow, @vtx

macro flow(ex)
  v = il(graphm(block(ex)))
  v = prewalkλ(withconst(x -> isexpr(x, :$) ? esc(x.args[1]) : Expr(:quote, x)), v)
  constructor(v)
end

macro vtx(ex)
  v = il(graphm(block(ex)))
  v = prewalkλ(withconst(esc), v)
  return constructor(v)
end