diff --git a/.ocamlformat b/.ocamlformat index 1dfb748..2da5f05 100644 --- a/.ocamlformat +++ b/.ocamlformat @@ -1 +1,2 @@ version=0.26.2 +parse-docstrings=true diff --git a/bin/player.ml b/bin/player.ml index dc2ab29..63d075f 100644 --- a/bin/player.ml +++ b/bin/player.ml @@ -18,14 +18,12 @@ let now_playing = Lwd.var None (* see https://github.com/jellyfin/jellyfin/blob/4786901bb796c3e912f13b686571fde8d16f49c5/tests/Jellyfin.Model.Tests/Test%20Data/DeviceProfile-Firefox.json *) (** Playback issue with alac files (and probably all codecs non-natively - supported by the browser) + supported by the browser) - Some clients (sonixd) give up, mpv based players work. - The official client manage to play the file but gives a blob to the audio player instead of a hls url as it does for other files. - Jellyfin should use the device id and associated capabilities to - automatically transcode, shouldn't it ? - -*) + automatically transcode, shouldn't it ? *) let audio_url (server : DS.connexion) item_id = Printf.sprintf "%s/Audio/%s/universal?api_key=%s&audioCodec=aac&container=opus,mp3,aac,m4a,m4b,flac,wav,ogg&transcodingContainer=ts&transcodingProtocol=hls" diff --git a/lib/db/sync.mli b/lib/db/sync.mli index 83b896e..23179c6 100644 --- a/lib/db/sync.mli +++ b/lib/db/sync.mli @@ -1,5 +1,4 @@ -(** - This module provide utilities to synchronize he local (Indexed_db) database +(** This module provide utilities to synchronize he local (Indexed_db) database with the remote source. The syncing mechanism makes the assumption that the source can provide a @@ -25,12 +24,10 @@ thus changing all following items' index in the reference query. TODO: inconsistency is not handled yet: the simplest workaround would be to - simply re-synchronize the complete database. + simply re-synchronize the complete database. TODO: we only keep track of added / removed items, but items details can - change (like changes in metadata, genrs, etc) - -*) + change (like changes in metadata, genrs, etc) *) type status = | Unknown @@ -57,4 +54,4 @@ val check_and_sync : Brrer.Brr_io.Indexed_db.Database.t -> (unit, Jv.Error.t) Fut.result (** [check_and_sync] cheks that the current db is consistent with the source and - makes appropriate updates if necessary *) + makes appropriate updates if necessary *) diff --git a/lib/db/view.ml b/lib/db/view.ml index f764625..90e2426 100644 --- a/lib/db/view.ml +++ b/lib/db/view.ml @@ -1,6 +1,8 @@ open Std -(** Some sorts require a custom ordering which is done using a table of indexes. For example, to get a random sort we simple shuffle an array which size is the one of the result. *) +(** Some sorts require a custom ordering which is done using a table of indexes. + For example, to get a random sort we simple shuffle an array which size is + the one of the result. *) module Order = struct type t = Initial | Asc | Desc | Custom of int array diff --git a/lib/worker_api/worker_api.mli b/lib/worker_api/worker_api.mli index 4987d91..7080a4b 100644 --- a/lib/worker_api/worker_api.mli +++ b/lib/worker_api/worker_api.mli @@ -2,10 +2,11 @@ module type Queries = sig type 'a query (** Use a GADT to describe queries that have different parameters and return value. For example: - {[ type 'a query = - | Next_int : int -> int query - | Previous_letters : char -> char list query ]} - *) + {[ + type 'a query = + | Next_int : int -> int query + | Previous_letters : char -> char list query + ]} *) type 'a event (** Events that can be listened to *) @@ -31,7 +32,7 @@ module Make : functor (Q : Queries) -> sig (** Send one query to the worker and return a future with the answer. *) val listen : 'a event -> f:('a -> unit) -> listener - (** It returns a [listener] handle that can be used to cancel the + (** It returns a [listener] handle that can be used to cancel the subscription later-on (TODO). *) end @@ -41,12 +42,11 @@ module Make : functor (Q : Queries) -> sig end val dispatch_event : 'a event -> 'a -> unit - (** [dispactch_event] should be used from inside the worker to send events - to the client. *) + (** [dispactch_event] should be used from inside the worker to send events to + the client. *) (** Use [Make_worker] to generate the body of the worker. The generative functor given in parameter contian the program t be executed, and should - provide an [on_query] function to answer all messages defined by the API. - *) + provide an [on_query] function to answer all messages defined by the API. *) module Make_worker : functor (_ : Worker_impl) -> sig end end diff --git a/vendor/brr_lwd_ui/brrer/indexed_db.mli b/vendor/brr_lwd_ui/brrer/indexed_db.mli index 2437d16..b99a549 100644 --- a/vendor/brr_lwd_ui/brrer/indexed_db.mli +++ b/vendor/brr_lwd_ui/brrer/indexed_db.mli @@ -84,8 +84,8 @@ module Content_access (Content : Store_content_intf) (Key : Key) : sig val continue : ?key:Content.Key.t -> t -> unit (** [continue t] advances the cursor to the next position along its - direction, to the item whose key matches the optional key parameter. This - will re-trigger the [on_success] event of the cursor's query. *) + direction, to the item whose key matches the optional key parameter. + This will re-trigger the [on_success] event of the cursor's query. *) end module Cursor_with_value : sig @@ -103,17 +103,16 @@ module Content_access (Content : Store_content_intf) (Key : Key) : sig val get_all : t -> Content.t Array.t Request.t (** [get_all] retrieves all objects that are inside the index. There is a - performance cost associated with looking at the value property of a cursor, - because the object is created lazily and [get_all] force the cration of all - objects in the store. + performance cost associated with looking at the value property of a + cursor, because the object is created lazily and [get_all] force the + cration of all objects in the store. - TODO: optional parameters *) + TODO: optional parameters *) val get_all_keys : ?query:Key_range.t -> t -> Content.Key.t Array.t Request.t - (** [get_all_keys] retrieves the list of all primary keys. + (** [get_all_keys] retrieves the list of all primary keys. - TODO stronger query type - TODO optional count parameter *) + TODO stronger query type TODO optional count parameter *) val fold_keys : init:'a -> @@ -218,54 +217,54 @@ val get_factory : ?global:Jv.t -> unit -> Factory.t (** Returns a [Database] factory. In some browsers it might be necessary to specify a global object (like [window]) with the [indexedDb] property. *) -(** Note about key comparisons: - -https://www.w3.org/TR/IndexedDB/#compare-two-keys - -To compare two keys a and b, run these steps: - - 1. Let ta be the type of a. - 2. Let tb be the type of b. - 3. If ta does not equal tb, then run these steps: - 1. If ta is array, then return 1. - 2. If tb is array, then return -1. - 3. If ta is binary, then return 1. - 4. If tb is binary, then return -1. - 5. If ta is string, then return 1. - 6. If tb is string, then return -1. - 7. If ta is date, then return 1. - 8. Assert: tb is date. - 9. Return -1. - - 4. Let va be the value of a. - 5. Let vb be the value of b. - 6. Switch on ta: - - number - - date - 1. If va is greater than vb, then return 1. - 2. If va is less than vb, then return -1. - 3. Return 0. - - string - 1. If va is code unit less than vb, then return -1. - 2. If vb is code unit less than va, then return 1. - 3. Return 0. - - binary - 1. If va is byte less than vb, then return -1. - 2. If vb is byte less than va, then return 1. - 3. Return 0. - - array - 1. Let length be the lesser of va’s size and vb’s size. - 2. Let i be 0. - 3. While i is less than length, then: - 1. Let c be the result of recursively comparing two keys with va[i] and vb[i]. - 2. If c is not 0, return c. - 3. Increase i by 1. - 4. If va’s size is greater than vb’s size, then return 1. - 5. If va’s size is less than vb’s size, then return -1. - 6. Return 0. - -The key a is greater than the key b if the result of comparing two keys with a and b is 1. - -The key a is less than the key b if the result of comparing two keys with a and b is -1. - -The key a is equal to the key b if the result of comparing two keys with a and b is 0.*) +(* Note about key comparisons: + + https://www.w3.org/TR/IndexedDB/#compare-two-keys + + To compare two keys a and b, run these steps: + + 1. Let ta be the type of a. + 2. Let tb be the type of b. + 3. If ta does not equal tb, then run these steps: + 1. If ta is array, then return 1. + 2. If tb is array, then return -1. + 3. If ta is binary, then return 1. + 4. If tb is binary, then return -1. + 5. If ta is string, then return 1. + 6. If tb is string, then return -1. + 7. If ta is date, then return 1. + 8. Assert: tb is date. + 9. Return -1. + + 4. Let va be the value of a. + 5. Let vb be the value of b. + 6. Switch on ta: + - number + - date + 1. If va is greater than vb, then return 1. + 2. If va is less than vb, then return -1. + 3. Return 0. + - string + 1. If va is code unit less than vb, then return -1. + 2. If vb is code unit less than va, then return 1. + 3. Return 0. + - binary + 1. If va is byte less than vb, then return -1. + 2. If vb is byte less than va, then return 1. + 3. Return 0. + - array + 1. Let length be the lesser of va’s size and vb’s size. + 2. Let i be 0. + 3. While i is less than length, then: + 1. Let c be the result of recursively comparing two keys with va[i] and vb[i]. + 2. If c is not 0, return c. + 3. Increase i by 1. + 4. If va’s size is greater than vb’s size, then return 1. + 5. If va’s size is less than vb’s size, then return -1. + 6. Return 0. + + The key a is greater than the key b if the result of comparing two keys with a and b is 1. + + The key a is less than the key b if the result of comparing two keys with a and b is -1. + + The key a is equal to the key b if the result of comparing two keys with a and b is 0.*) diff --git a/vendor/brr_lwd_ui/brrer/media_session.mli b/vendor/brr_lwd_ui/brrer/media_session.mli index d052399..d5d24e9 100644 --- a/vendor/brr_lwd_ui/brrer/media_session.mli +++ b/vendor/brr_lwd_ui/brrer/media_session.mli @@ -31,10 +31,10 @@ end val set_action_handler : t -> Action.t -> (unit -> unit) -> unit (** Sets a handler for a media session action. These actions let a web app - receive notifications when the user engages a device's built-in physical or - onscreen media controls, such as play, stop, or seek buttons. + receive notifications when the user engages a device's built-in physical or + onscreen media controls, such as play, stop, or seek buttons. - https://developer.mozilla.org/en-US/docs/Web/API/MediaSession/setActionHandler#callback *) + https://developer.mozilla.org/en-US/docs/Web/API/MediaSession/setActionHandler#callback *) val set_position_state : ?duration:float -> ?playback_rate:float -> ?position:float -> t -> unit diff --git a/vendor/brr_lwd_ui/lib/common/attrs.ml b/vendor/brr_lwd_ui/lib/common/attrs.ml index fa11f57..37e318d 100644 --- a/vendor/brr_lwd_ui/lib/common/attrs.ml +++ b/vendor/brr_lwd_ui/lib/common/attrs.ml @@ -33,7 +33,8 @@ open Brr open Brr_lwd type t = { classes : Classes.t; attrs : At.t Elwd.col } -(** Classes attributes are handled separately but are eventually translated to At.t *) +(** Classes attributes are handled separately but are eventually translated to + At.t *) let empty = { classes = Classes.empty; attrs = [] } diff --git a/vendor/brr_lwd_ui/lib/common/logger.ml b/vendor/brr_lwd_ui/lib/common/logger.ml index 51f1aec..8de15fc 100644 --- a/vendor/brr_lwd_ui/lib/common/logger.ml +++ b/vendor/brr_lwd_ui/lib/common/logger.ml @@ -1,5 +1,4 @@ open Import - open Brr type section = .. diff --git a/vendor/brr_lwd_ui/lib/common/persistent.mli b/vendor/brr_lwd_ui/lib/common/persistent.mli index 7534c1d..2a3a992 100644 --- a/vendor/brr_lwd_ui/lib/common/persistent.mli +++ b/vendor/brr_lwd_ui/lib/common/persistent.mli @@ -3,11 +3,11 @@ val local_storage : Brr_io.Storage.t val var : key:string -> 'a -> 'a Lwd.var (** [var ~key v] returns a new [Lwd.var]. - If [key] exists in the local_storage, the stored value is used as initial - value instead of [v]. When this var's value is changed using the [Lwd.set] - function its new value is persisted into the local_storage at the key [key]. + If [key] exists in the local_storage, the stored value is used as initial + value instead of [v]. When this var's value is changed using the [Lwd.set] + function its new value is persisted into the local_storage at the key [key]. - A uniqueness check on [key] is performed at runtime. *) + A uniqueness check on [key] is performed at runtime. *) val var_f : key:string -> (unit -> 'a) -> 'a Lwd.var (** Same as [var] but uses a callback to initialize the variable when needed. *) diff --git a/vendor/brr_lwd_ui/lib/controlled_scroll/controlled_scroll.mli b/vendor/brr_lwd_ui/lib/controlled_scroll/controlled_scroll.mli index b22689b..6219221 100644 --- a/vendor/brr_lwd_ui/lib/controlled_scroll/controlled_scroll.mli +++ b/vendor/brr_lwd_ui/lib/controlled_scroll/controlled_scroll.mli @@ -4,7 +4,7 @@ open Brr open Brr_lwd (** The target of a controlled scroll: can be a position in pixels or an DOM - element. *) + element. *) type target = Pos of int | El of El.t val make : @@ -15,12 +15,12 @@ val make : El.t Lwd.t -> El.t Lwd.t (** [make ?at ~scroll_target elt] will wrap [elt] in a div containing a floating - button. When the button is pressed the [elt] scroll position will react to - changes of [scroll_target]. Control is given back to the user as soon a manual - scrolling is initiated. + button. When the button is pressed the [elt] scroll position will react to + changes of [scroll_target]. Control is given back to the user as soon a + manual scrolling is initiated. - TODO: - - More configuration options - - Customizable button list - - Control an element that is a child of [elt] - - Default behavior for "scroll to top" *) + TODO: + - More configuration options + - Customizable button list + - Control an element that is a child of [elt] + - Default behavior for "scroll to top" *) diff --git a/vendor/brr_lwd_ui/lib/forms/form.mli b/vendor/brr_lwd_ui/lib/forms/form.mli index 4f73745..905206c 100644 --- a/vendor/brr_lwd_ui/lib/forms/form.mli +++ b/vendor/brr_lwd_ui/lib/forms/form.mli @@ -12,8 +12,8 @@ module type Form = sig end val field : 'a Field.t Lwd.t -> ('b, 'a) form_setter -> 'b form_field Lwd.t -(** Attach a new field to a form with a function that sets the form's state - with values from that field. *) +(** Attach a new field to a form with a function that sets the form's state with + values from that field. *) val create : ?d:Brr.El.document -> diff --git a/vendor/brr_lwd_ui/lib/table/FFCache.ml b/vendor/brr_lwd_ui/lib/table/FFCache.ml index 8b6a6bd..e9baffe 100644 --- a/vendor/brr_lwd_ui/lib/table/FFCache.ml +++ b/vendor/brr_lwd_ui/lib/table/FFCache.ml @@ -61,13 +61,12 @@ module Make (Key : Map.OrderedType) : S with type key = Key.t = struct let q2 = RA_queue.create () in { q1; q2; size } - (** [evict_one t] first tries to evict the last element of [t.q2]. - If that last element has been visited, it is moved to the head of [t.q1]. - Loop until an element is evicted or [t.q2] is empty. - If [t.q2] is empty we perform the same process by inversing the roles of - [t.q2] and [t.q1]. + (** [evict_one t] first tries to evict the last element of [t.q2]. If that + last element has been visited, it is moved to the head of [t.q1]. Loop + until an element is evicted or [t.q2] is empty. If [t.q2] is empty we + perform the same process by inversing the roles of [t.q2] and [t.q1]. - /!\ This function will loop if both queues are empty. *) + /!\ This function will loop if both queues are empty. *) let rec evict_one ~on_evict t = evict_q2 ~on_evict t and evict_q2 ~on_evict t = diff --git a/vendor/brr_lwd_ui/lib/table/FFCache.mli b/vendor/brr_lwd_ui/lib/table/FFCache.mli index d0161a3..10b1cc3 100644 --- a/vendor/brr_lwd_ui/lib/table/FFCache.mli +++ b/vendor/brr_lwd_ui/lib/table/FFCache.mli @@ -1,5 +1,5 @@ -(** A simple bounded cache with an eviction mechanism using 2 FIFOs. - Inspired by SIEVE. Unproven. Untested. *) +(** A simple bounded cache with an eviction mechanism using 2 FIFOs. Inspired by + SIEVE. Unproven. Untested. *) module type S = sig type key diff --git a/vendor/brr_lwd_ui/lib/table/virtual_table.ml b/vendor/brr_lwd_ui/lib/table/virtual_table.ml index 2ea679c..9880e27 100644 --- a/vendor/brr_lwd_ui/lib/table/virtual_table.ml +++ b/vendor/brr_lwd_ui/lib/table/virtual_table.ml @@ -2,8 +2,8 @@ TODO: this is clearly over-engineered: the large lwd table that reduces to rows and placeholders with a monoid is elegant bu does not scale well. It - might be possible to optimize it (especially the "uniqueue" LRU thingy), - but having too large a lwd_table is probably a hard limit. *) + might be possible to optimize it (especially the "uniqueue" LRU thingy), but + having too large a lwd_table is probably a hard limit. *) open Import open Brrer @@ -295,26 +295,26 @@ let make (type data) ~(ui_table : Schema.fixed_row_height) table (** #######**#******#%%#===+++*###%###########*+##=###++++++++++++++++++++++++++ -###########*####****%%##===========+#===-=======*#=###++++++++++++++++++++++++++ -###########*#####***#%##======---==+#==---======*#=###++++++++++++++++++++++++++ -########################====----==-=#=--========*#=*##%#+++++%%%++++++++++++++++ -######################%#====-====--=#===========*#=*##%#++=+#**%%+++++++++++++++ -#######################*=----------=#===========+#=+##+%#%*+%+%%%+++++++++++++++ -###################+=--------==-----#====-==----+#=+##*+%#%%+##%%+++++++++++++++ -#################+=-------------------------==-=+#=+###+++#*%+#+++++++++++++++++ -################=---------=####=#=--#=++====--===#=+###*+++%%++%%+++++++++++++++ -##############*======--==**######+=====+=++++++*##=+###*++++++++++++++++++++++++ -##############=-----==+*+###*######=#==----=====+#=+###****++++++++IS+++++++++++ -##############==-=-=*#+=#+%+#===#===#==--========#=+###***++++++++++++THIS++++++ -##############=-#*#+*##=####*+#=++==#===========+#=+###*******+++A++++++++++++++ -##############==###=############+#*+#============#++###*********++++++++++++++++ -###############=*+#*###############=#============#++###************MONOID+?+++++ -################==##########====##*=#=======#**==#++###*****************++++++++ -#################==+##############==#=====###====#++###**********************+++ -##################==###+#########+###*+==###=====#++##+++**********************+ -##############+%%%%########+####*==+#==+##############*++*********************** -##############+++++=+%#%#*###*#%%%%+#=+##########*###+#=##*#******************** -##############*#==+====##===###%%%%%%%#############=#+######****++*==+********** -#####+=+###########=+==+====###*+#%%%%###########*++########==*+======++******** -####################*=======#####+#%%*########*==#++######*================***** -############+#####+####===+=#######+########=====#++###+===================+***) + ###########*####****%%##===========+#===-=======*#=###++++++++++++++++++++++++++ + ###########*#####***#%##======---==+#==---======*#=###++++++++++++++++++++++++++ + ########################====----==-=#=--========*#=*##%#+++++%%%++++++++++++++++ + ######################%#====-====--=#===========*#=*##%#++=+#**%%+++++++++++++++ + #######################*=----------=#===========+#=+##+%#%*+%+%%%+++++++++++++++ + ###################+=--------==-----#====-==----+#=+##*+%#%%+##%%+++++++++++++++ + #################+=-------------------------==-=+#=+###+++#*%+#+++++++++++++++++ + ################=---------=####=#=--#=++====--===#=+###*+++%%++%%+++++++++++++++ + ##############*======--==**######+=====+=++++++*##=+###*++++++++++++++++++++++++ + ##############=-----==+*+###*######=#==----=====+#=+###****++++++++IS+++++++++++ + ##############==-=-=*#+=#+%+#===#===#==--========#=+###***++++++++++++THIS++++++ + ##############=-#*#+*##=####*+#=++==#===========+#=+###*******+++A++++++++++++++ + ##############==###=############+#*+#============#++###*********++++++++++++++++ + ###############=*+#*###############=#============#++###************MONOID+?+++++ + ################==##########====##*=#=======#**==#++###*****************++++++++ + #################==+##############==#=====###====#++###**********************+++ + ##################==###+#########+###*+==###=====#++##+++**********************+ + ##############+%%%%########+####*==+#==+##############*++*********************** + ##############+++++=+%#%#*###*#%%%%+#=+##########*###+#=##*#******************** + ##############*#==+====##===###%%%%%%%#############=#+######****++*==+********** + #####+=+###########=+==+====###*+#%%%%###########*++########==*+======++******** + ####################*=======#####+#%%*########*==#++######*================***** + ############+#####+####===+=#######+########=====#++###+===================+***) diff --git a/vendor/brr_lwd_ui/lib/table/virtual_table_bis.ml b/vendor/brr_lwd_ui/lib/table/virtual_table_bis.ml index fb7f275..9bb4f9f 100644 --- a/vendor/brr_lwd_ui/lib/table/virtual_table_bis.ml +++ b/vendor/brr_lwd_ui/lib/table/virtual_table_bis.ml @@ -2,8 +2,8 @@ TODO: this is clearly over-engineered: the large lwd table that reduces to rows and placeholders with a monoid is elegant bu does not scale well. It - might be possible to optimize it (especially the "uniqueue" LRU thingy), - but having too large a lwd_table is probably a hard limit. *) + might be possible to optimize it (especially the "uniqueue" LRU thingy), but + having too large a lwd_table is probably a hard limit. *) open Import open Brrer @@ -319,26 +319,26 @@ let make (type data) ~(ui_table : Schema.fixed_row_height) table (** #######**#******#%%#===+++*###%###########*+##=###++++++++++++++++++++++++++ -###########*####****%%##===========+#===-=======*#=###++++++++++++++++++++++++++ -###########*#####***#%##======---==+#==---======*#=###++++++++++++++++++++++++++ -########################====----==-=#=--========*#=*##%#+++++%%%++++++++++++++++ -######################%#====-====--=#===========*#=*##%#++=+#**%%+++++++++++++++ -#######################*=----------=#===========+#=+##+%#%*+%+%%%+++++++++++++++ -###################+=--------==-----#====-==----+#=+##*+%#%%+##%%+++++++++++++++ -#################+=-------------------------==-=+#=+###+++#*%+#+++++++++++++++++ -################=---------=####=#=--#=++====--===#=+###*+++%%++%%+++++++++++++++ -##############*======--==**######+=====+=++++++*##=+###*++++++++++++++++++++++++ -##############=-----==+*+###*######=#==----=====+#=+###****++++++++IS+++++++++++ -##############==-=-=*#+=#+%+#===#===#==--========#=+###***++++++++++++THIS++++++ -##############=-#*#+*##=####*+#=++==#===========+#=+###*******+++A++++++++++++++ -##############==###=############+#*+#============#++###*********++++++++++++++++ -###############=*+#*###############=#============#++###************MONOID+?+++++ -################==##########====##*=#=======#**==#++###*****************++++++++ -#################==+##############==#=====###====#++###**********************+++ -##################==###+#########+###*+==###=====#++##+++**********************+ -##############+%%%%########+####*==+#==+##############*++*********************** -##############+++++=+%#%#*###*#%%%%+#=+##########*###+#=##*#******************** -##############*#==+====##===###%%%%%%%#############=#+######****++*==+********** -#####+=+###########=+==+====###*+#%%%%###########*++########==*+======++******** -####################*=======#####+#%%*########*==#++######*================***** -############+#####+####===+=#######+########=====#++###+===================+***) + ###########*####****%%##===========+#===-=======*#=###++++++++++++++++++++++++++ + ###########*#####***#%##======---==+#==---======*#=###++++++++++++++++++++++++++ + ########################====----==-=#=--========*#=*##%#+++++%%%++++++++++++++++ + ######################%#====-====--=#===========*#=*##%#++=+#**%%+++++++++++++++ + #######################*=----------=#===========+#=+##+%#%*+%+%%%+++++++++++++++ + ###################+=--------==-----#====-==----+#=+##*+%#%%+##%%+++++++++++++++ + #################+=-------------------------==-=+#=+###+++#*%+#+++++++++++++++++ + ################=---------=####=#=--#=++====--===#=+###*+++%%++%%+++++++++++++++ + ##############*======--==**######+=====+=++++++*##=+###*++++++++++++++++++++++++ + ##############=-----==+*+###*######=#==----=====+#=+###****++++++++IS+++++++++++ + ##############==-=-=*#+=#+%+#===#===#==--========#=+###***++++++++++++THIS++++++ + ##############=-#*#+*##=####*+#=++==#===========+#=+###*******+++A++++++++++++++ + ##############==###=############+#*+#============#++###*********++++++++++++++++ + ###############=*+#*###############=#============#++###************MONOID+?+++++ + ################==##########====##*=#=======#**==#++###*****************++++++++ + #################==+##############==#=====###====#++###**********************+++ + ##################==###+#########+###*+==###=====#++##+++**********************+ + ##############+%%%%########+####*==+#==+##############*++*********************** + ##############+++++=+%#%#*###*#%%%%+#=+##########*###+#=##*#******************** + ##############*#==+====##===###%%%%%%%#############=#+######****++*==+********** + #####+=+###########=+==+====###*+#%%%%###########*++########==*+======++******** + ####################*=======#####+#%%*########*==#++######*================***** + ############+#####+####===+=#######+########=====#++###+===================+***)