Envision Language Server - Envision function signatures

All currently active functions signatures, by category.

module actionrwd {
    /// Returns a ranvar that represents the cumulative demand
    /// associated to the trajectories as generated by 'actionrwd.reward'.
    call demand<Items, Periods>(
        /// Defines a non-ambiguous ordering per item (i.e. distinct values required).
        Periods.TimeIndex: number as "TimeIndex",
        /// The baseline of the average demand over each period.
        Periods.Baseline: number as "Baseline",
        /// The dispersion parameter (variance divided by mean) of the demand for each item.
        Items.Dispersion: number as "Dispersion",
        /// The update speed parameter of the ISSM model for each item.
        Items.Alpha: number as "Alpha",
        /// Number of trajectories used to evaluate the cumulative demand.
        /// The parameter has to be defined within [1, 10'000]. Default: 2'500.
        scalar.Samples?: number as "Samples",  
        /// Seed used for the trajectory generator.
        scalar.Seed?: number as "Seed",    
        Items -> Periods) : Periods.ranvar as "actionrwd.demand"

    /// Returns the fraction of the upstream demand which get serviced from
    /// the downstream stock in a 2-echelon network.
    call dampen<Items, DownItems, Periods>(
        /// The dispersion parameter (variance divided by mean) of the demand.
        Items.Dispersion: number as "Dispersion",
        /// The update speed parameter of the ISSM model, ranges within [0, 1].
        Items.Alpha: number as "Alpha",
        /// The downstream stock on hand.
        DownItems.StockOnHand: number as "StockOnHand",
        /// Defines a non-ambiguous ordering per downItem (i.e. distinct values required).
        Periods.TimeIndex: number as "TimeIndex",
        /// The baseline of the average demand over each period.
        Periods.Baseline: number as "Baseline",
        /// Number of trajectories used to evaluate the cumulative demand.
        /// The parameter has to be defined within [1, 10'000]. Default: 2'500.
        scalar.Samples?: number as "Samples",
        /// Seed used for the trajectory generator.
        scalar.Seed?: number as "Seed",
        Items -> Periods,
        DownItems -> Periods) : Items.zedfunc as "actionrwd.dampen"

    /// Returns a ranvar that represents the integrated demand - over the segment -
    /// associated to the trajectories as generated by 'actionrwd.reward'.
    call segment<Items, Periods, Segments>(
        /// Defines a non-ambiguous ordering per item (i.e. distinct values required).
        Periods.TimeIndex: number as "TimeIndex",
        /// The baseline of the average demand over each period.
        Periods.Baseline: number as "Baseline",
        /// The dispersion parameter (variance divided by mean) of the demand for each item.
        Items.Dispersion: number as "Dispersion",
        /// The update speed parameter of the ISSM model for each item.
        Items.Alpha: number as "Alpha",
        /// The inclusive start of the segment expressed in periods (zero indexed).
        Segments.Start: ranvar as "Start",
        /// The length of the segment expressed in number of periods.
        Segments.Duration: ranvar as "Duration",
        /// Number of trajectories used to evaluate the cumulative demand.
        /// The parameter has to be defined within [1, 10'000]. Default: 2'500.
        scalar.Samples?: number as "Samples",  
        /// Seed used for the trajectory generator.
        scalar.Seed?: number as "Seed",    
        /// The inclusive start of the segment, expressed as a pure number.
        /// If provided, 'Start' will be interpreted as a 'variation' around
        /// 'FixedStart'.
        Segments.FixedStart?: number as "FixedStart",    
        Items -> Periods,
        Items -> Segments) : Segments.ranvar as "actionrwd.segment"

    /// Returns two zedfuncs associated to the marginal outcome of ordering units. 
    /// The space considered is the ordering space.
    call reward<Items, Periods, Orders>(
        /// Defines a non-ambiguous ordering per item (i.e. distinct values required).
        Periods.TimeIndex: number as "TimeIndex",
        /// The baseline of the average demand over each period.
        Periods.Baseline: number as "Baseline",
        /// The dispersion parameter (variance divided by mean) of the demand for each item.
        Items.Dispersion: number as "Dispersion",
        /// The update speed parameter of the ISSM model for each item.
        Items.Alpha: number as "Alpha",  
        /// Stock on hand at the time the order is placed.
        Items.StockOnHand: number as "StockOnHand",
        /// Lead time, unit is abitrary time step, must be consistent with StepOfReorder.
        /// - Provided alone [legacy]: lead times used in the computation will be deviates sampled
        ///   from this distribution;
        /// - In conjunction with "NumLeadTime": lead times are still drawn from this
        ///   distribution, but deviates will be shifted by the fixed amount defined in
        ///   "NumLeadTime".
        Items.LeadTime?: ranvar as "LeadTime",
        /// Estimated next reorder time, in arbitrary time steps.
        Items.StepOfReorder: number as "StepOfReorder",
        /// Probability for each unit of demand to be covered by downstream stock.
        Items.StockOffHand?: zedfunc as "StockOffHand",
        /// Estimated order's arrival period, zero-indexed.
        /// ArrivalTime and StockOnOrder must be both present, or both absent.
        Orders.ArrivalTime?: ranvar as "ArrivalTime",
        /// Quantity associated to the pending order. Stock is assumed available at the beginning of the period. 
        Orders.StockOnOrder?: number as "StockOnOrder",
        /// Number of trajectories used to evaluate to action reward.
        /// The parameter has to be defined within [1, 10'000]. Default: 2'500.
        scalar.Samples?: number as "Samples",  
        /// Seed used for the trajectory generator.
        scalar.Seed?: number as "Seed",
        /// Lead time represented as a fixed numerical value.
        /// If provided alone, the fixed lead time specified will be the only
        /// value used in the computation. Please refer to "LeadTime" documentation
        /// for description of additional cases.
        Items.NumLeadTime?: number as "NumLeadTime",
        Items -> Periods,
        ?Items -> Orders) : {
            /// Serviceable demand on the responsibility window.
            Items.Demand: ranvar,
            /// Mean estimate of the number of periods of carrying cost for the nth
            /// ordered unit.
            Items.HoldingTime: zedfunc
        } as "actionrwd.reward"
}
module assoc {
    /// Associates quantities from two sets connected by edges.
    ///
    /// Given two sets, left and right, represented by the `l` and
    /// `r` groupings, the function takes the list of (left, right) edges
    /// connecting the two sets. Each set element has an associated initial 
    /// quantity `same(lq) by l` or `same(rq) by r`.  This quantity 
    /// decreases over the course of the algorithm.
    ///
    /// Edges are visited in the specified order. The return value for the 
    /// edge is set to the minimum of the two nodes' current quantity (the
    /// associated quantity), and that value is then subtracted from both 
    /// quantities. In consequence, once a node's quantity reaches zero, it
    /// no longer contributes to the algorithm.
    ///
    /// If a 'by' grouping is provided, each group is treated independently
    /// from all others. 
    ///
    call quantity(
        l: grouping,
        lq: number,
        r: grouping,
        rq: number,
        item: by grouping,
        visit: sort ordering): number as "assoc.quantity"
}

/// The canonical name after a renaming.
///
/// Assume that `old` and `new`, within the same table, 
/// represent replacement rules between old and new values. 
/// For each value in `old`, this function returns the 
/// latest value, once all replacement rules have been applied.
///
/// More formally:
/// 
/// > Let A → B be the replacement transition (as expressed by 
/// > the input vectors).
/// >
/// > Let A →* B be the usual starred transition (either A = B 
/// > or there exists a C such that A → C →* B).
/// >
/// > Define that B is a terminal replacement of A if A →* B and 
/// > there is no C such that B → C. 
///
/// This function requires that each node A has one and only one 
/// terminal replacement B, which is called the canonical 
/// replacement of A. 
call canonical(old: text, new: text, g: by grouping) : text as "canonical"

/// True for each renaming pair that breaks `canonical`
/// 
/// Assume that `old` and `new`, within the same table, 
/// represent replacement rules between old and new values. 
///
/// `canonical()` requires that each value has one and only one 
/// terminal replacement. This function returns false for each
/// replacement rule which:
///
/// 1. creates a replacement cycle (a value replaced, directly 
///    or indirectly, with itself), or
/// 2. causes a second replacement to exist for a given value
///
/// Filtering out false values ensures that `canonical()` can 
/// be called without error.
call noncanonical(old: text, new: text, g: by grouping) : boolean as "noncanonical"
///  Computes the action reward given a periodic forecast

/// Return true for edges presented in the cycle,
/// otherwise return false.
call hasCycles(src: text, dst: text, g: by grouping) : boolean as "hasCycles"

/// Returns true for one last value of the group according
/// to the given ordering. If the group is empty due to the
/// condition, you may have only `false`.
call arglast(o: sort ordering, g: by grouping, condition: boolean) : boolean as "arglast(if)"

/// Generates one dashboard slice for each unique entry in the grouping, using
/// `same(key)` as the searchable key for that slice. May only be called once
/// in a script.
call sliceDashboard(key: text, g: by grouping) : ordinal as builtin

/// Generates one dashboard slice for each unique entry in the grouping, using
/// `same(key)` and `same(subtitle)` as the searchable key and subtitle for that 
/// slice. May only be called once in a script.
call sliceDashboard(key: text, subtitle: text, g: by grouping) : ordinal as builtin

/// Parse a text containing a date
call parseDate(raw: text) : date as "parseDate"

/// Parses text containing a number
call parseNumber(raw: text) : number as "parseNumber"

/// Divides each value by the sum of all value
call percent(values: number, group: by grouping) : number as "percent"

/// Ranks values in descending order, starting at 1.
call rank(
    score: any,
    groups: grouping,
    sequence: ordering) : number as "rank(seq)"

/// Ranks values in descending order, starting at 1.
call prioStack(
    score: any,
    groups: grouping,
    sequence: ordering) : number as "rank(seq)"

/// Ranks distinct values in descending order, starting at 1.
call rankd(
    score: any,
    groups: by grouping) : number as "rankd"

/// A simple variant of the bin packing algorithm intended
/// to be used with purchase prioritization list
call prioPack(
    order: sort ordering,
    containerGroup: by grouping,
    /// Volume of each line
    volume: number,
    /// `maxContainerVolume` is the max volume capacity, its value is
    /// homogeneous to `volume`, and it is assumed to be a constant value
    /// across the equivalent class `Group`.
    maxContainerVolume: number,
    /// `jumpThreshold` values is homogeneous to `Volume`,
    /// it is typically expected to a small multiple of the
    /// `Group` value.
    jumpThreshold: number,
    /// when this value is provided, the bin-packing process
    /// is not allowed to reorder lines that below to the same
    /// equivalence class as defined by `barrier`.
    barrier: grouping) : number as "priopack"

/// Returns the ranvar of the stock after a series of demands and deliveries.
/// Order matters since stock cannot be negative.
call stocktrace<Items, Periods>(
    /// the initial stock for each item
    Items.Initial: number,
    /// The list of demand ranvars 
    Periods.Demand: ranvar,
    /// The list of deliveries between the demands
    Periods.Delivery: number,
    Items -> Periods) : Items.ranvar as "stocktrace"

/// Returns the ranvar of the stock after a series of demands and deliveries.
/// Order matters since stock cannot be negative.
/// Max and Min values of stock should be specified for each period. 
/// Use Max to represent expiration of part of the stock. 
call stocktrace<Items, Periods>(
    /// the initial stock for each item
    Items.Initial: number,
    /// The list of demand ranvars 
    Periods.Demand: ranvar,
    /// The list of deliveries between the demands
    Periods.Delivery: number,
    /// The maximal stock at the end of each period
    Periods.MinStock : number,
    /// the minimal stock at the end of each period
    Periods.MaxStock : number,
    Items -> Periods) : Items.ranvar as "stocktracebounds"

call fifo<Po, Items>(
    Items.stock: number,
    Po.date: date,
    Po.quantity: number,
    Items -> Po) : Po.number as "fifo"

module forest {
    call regress<Evaluation, Training>(
        Training.training*: any as "training",
        Training.trainingBow?: text as "trainingbow",
        Training.label: number as "label",
        Evaluation.evaluation*: any as "evaluation",
        Evaluation.evaluationBow?: text as "evaluationBow") : Evaluation.ranvar as "forest.regress"
}

module proto {
    call remote<Sclr,Files>(
        Files.Path: text as "Path",
        Files.Hash: text as "Hash",
        Sclr.Arguments?: text as "Arguments",
        Sclr.Remote: text as "Remote",
        Sclr.Write: text as "Write") : Sclr.number as "proto.remote"
}

/// Returns a unique number, with uniqueness maintained across Envision runs.
/// This function is intended to uniquely identify results calculated by Lokad.
/// For example, it can be used to generate a unique purchase order number to
/// be incremented whenever the Envision script is re-executed. 
call mkuid(
    /// Ignored argument, only used to infer the correct tables.
    any: any,
    /// optional scalar that represents the starting suffix for for the UID.
    /// The generated strings are numbers in format PPPPPPPAAA, with P a page
    /// number (does not start with 0) that is always strictly increasing,
    /// and A an incremented counter that starts at offset (or 0 if no offset
    /// parameter is provided). P has at least 7 digits, A has at least 3. 
    scalar.offset: number) : text as "mkuid(offset)"

/// Returns a unique number, with uniqueness maintained across Envision runs.
/// This function is intended to uniquely identify results calculated by Lokad.
/// For example, it can be used to generate a unique purchase order number to
/// be incremented whenever the Envision script is re-executed. 
call mkuid(
    /// Ignored argument, only used to infer the correct tables.
    any: any) : text as "mkuid"

/// The function returns 
/// - the positive decremented stock levels if there are enough for the group
/// - the negative (-1) non-decremented stock levels if there are not enough for the group.
call cumsub(
    /// item identifier, all lines that share the same value belong to the same item;
    item: any,
    /// initial stock for the item, all lines that belong to the same
    /// `item` must have the same `stock` value
    stock: number,
    /// quantity of the item required for the purchase of the grid line
    quantity: number,
    /// bundle identifier, all lines that share the same bundle identifier
    /// belong to the same bundle, it is forbidden to have two lines with
    /// the same (`Item`, `rank`) pair, and all bundles are ordered by increasing rank
    rank: number) : number as "cumsub"

/// Considers the undirected graph described by all edges (A,B), then
/// returns for each node A the name of the smallest node in A's
/// connected component. Here, "smallest" means having the smallest name,
/// in terms of string comparison. 
/// 
/// When a `by` grouping is specified, nodes with the same name, but from 
/// distinct groups, are considered to be distinct nodes.
///
call connected(
    /// Start point of edges
    leftV : text,
    /// End point of edges.
    rightV : text,
    slices : by grouping) : text as "connected"

[sideeffect] map (+)(left: embedding, right: embedding) : embedding as "add(emb,emb)"
[sideeffect] map (-)(left: embedding, right: embedding) : embedding as "sub(emb,emb)"
[sideeffect] map (*)(left: embedding, right: embedding) : embedding as "mul(emb,emb)"
[sideeffect] map (==)(left: embedding, right: embedding) : boolean as "eq(emb,emb)"
[sideeffect] map (!=)(left: embedding, right: embedding) : boolean as "neq(emb,emb)"
[sideeffect, restricted] map similarity(left: embedding, right: embedding) : number as "similarity"

/// Returns the embedding of a string. Indices are expected to be in the range [0, 512[.
[restricted] call embedding(emb: text) : embedding as "embedding(txt)"
module forecast {

    call leadtime<Items, Demand, Censored, Inflated, Promotion, LeadTime>(
        Items.Horizon?: ranvar as "Horizon",
        Items.Hierarchy*: text as "Hierarchy",
        Items.Category*: text as "Category",
        Items.Label?: text as "Label",
        Items.Location?: text as "Location",
        Items.Ref?: text as "Ref",
        Items.DemandStartDate?: date as "DemandStartDate",
        Items.DemandEndDate?: date as "DemandEndDate",
        Items.ItemName?: text as "ItemName",

        /// Not to be provided for leadtime forecast
        Demand.DemandDate?: date as "DemandDate", 
        /// Not to be provided for leadtime forecast
        Demand.DemandValue?: number as "DemandValue", 

        /// Not to be provided for leadtime forecast
        ?Items -> Demand,

        /// Not to be provided for leadtime forecast
        Censored.CensoredDemandDate?: date as "CensoredDemandDate", 
        /// Not to be provided for leadtime forecast
        Censored.CensoredFraction?: number as "CensoredFraction", 
        /// Not to be provided for leadtime forecast
        ?Items -> Censored,

        /// Not to be provided for leadtime forecast
        Inflated.InflatedDemandDate?: date as "InflatedDemandDate", 
        /// Not to be provided for leadtime forecast
        ?Items -> Inflated,

        /// Not to be provided for leadtime forecast
        Promotion.PromotionDate?: date as "PromotionDate",
        /// Not to be provided for leadtime forecast
        Promotion.PromotionDiscount?: number as "PromotionDiscount",
        /// Not to be provided for leadtime forecast
        Promotion.PromotionCategory?: text as "PromotionCategory",
        /// Not to be provided for leadtime forecast
        ?Items -> Promotion, 

        Leadtime.LeadtimeSupplier?: text as "LeadtimeSupplier",
        Leadtime.Date: date as "LeadtimeDate",
        Leadtime.Value?: number as "LeadtimeValue",

        Items -> LeadTime,

        scalar.Present: date as "Present",

        Items.Offset?: number as "Offset",
        Items.Supplier?: text as "Supplier",
        scalar.Engine?: text as "Engine",
        scalar.Write?: text as "Write"
    ) : Items.ranvar as "forecast.leadtime"

    call demand<Items, Demand, Censored, Inflated, Promotion, LeadTime>(
        Items.Horizon: ranvar as "Horizon",
        Items.Hierarchy*: text as "Hierarchy",
        Items.Category*: text as "Category",
        Items.Label?: text as "Label",
        Items.Location?: text as "Location",
        Items.Ref?: text as "Ref",
        Items.DemandStartDate?: date as "DemandStartDate",
        Items.DemandEndDate?: date as "DemandEndDate",
        Items.ItemName?: text as "ItemName",

        Demand.DemandDate: date as "DemandDate",
        Demand.DemandValue: number as "DemandValue",

        Items -> Demand,

        Censored.CensoredDemandDate*: date as "CensoredDemandDate",
        /// The fraction of demand that was censored (0 < fraction <= 1)
        Censored.CensoredFraction?: number as "CensoredFraction", 
        ?Items -> Censored,

        Inflated.InflatedDemandDate*: date as "InflatedDemandDate",
        ?Items -> Inflated,

        Promotion.PromotionDate*: date as "PromotionDate",
        Promotion.PromotionDiscount?: number as "PromotionDiscount",
        Promotion.PromotionCategory?: text as "PromotionCategory",
        ?Items -> Promotion,

        /// Not to be provided for demand forecast
        Leadtime.LeadtimeSupplier?: text as "LeadtimeSupplier",
        /// Not to be provided for demand forecast
        Leadtime.Date?: date as "LeadtimeDate",
        /// Not to be provided for demand forecast
        Leadtime.Value?: number as "LeadtimeValue",
        /// Not to be provided for demand forecast
        ?Items -> LeadTime,

        scalar.Present: date as "Present",

        Items.Offset?: number as "Offset",
        Items.Supplier?: text as "Supplier",
        scalar.Engine?: text as "Engine",
        scalar.Write?: text as "Write"
    ) : Items.ranvar  as "forecast.demand"
}
module loglikelihood {
    /// Logarithm of the likelihood of a normal distribution.
    map normal(
        /// Mean of the normal distribution.
        mu : number,
        /// Standard deviation of the normal distribution.
        sigma : number,
        /// Observed value.
        x : number) : number as "loglikelihood-normal"

    /// Logarithm of the likelihood of a Poisson distribution.
    map poisson(
        /// Mean of the Poisson distribution, must be positive.
        lambda : number, 
        /// Observed value. A non-negative integer.
        k : number) : number as "loglikelihood-poisson"

    /// Logarithm of the likelihood of a negative binomial distribution.
    map negativeBinomial(
        /// Mean of the negative binomial distribution, must be positive.
        mean : number,
        /// Dispersion. Greater or equal to 1. Defined by the relation
        /// 'variance = dispersion * mean'. When equal to 1, a fallback
        /// on the Poisson distribution is used.
        dispersion : number,
        /// Observed value. A non-negative integer.
        k : number) : number as "loglikelihood-negativebinomial"

    /// Logarithm of the likelihood of a zero inflated negative binomial 
    /// distribution (mixture of a negative binomial and a dirac in zero).
    map negativeBinomial(
        /// Mean of the negative binomial distribution component, 
        /// must be positive.
        mean : number,
        /// Dispersion. Greater or equal to 1. Defined by the relation
        /// 'variance = dispersion * mean'. When equal to 1, a fallback
        /// on the Poisson distribution is used.
        dispersion : number,
        /// ZeroInflation. Must be between 0 and 1. Weight of the dirac 
        /// in zero.
        dispersion : number,
        /// Observed value. A non-negative integer.
        k : number) : number as "loglikelihood-inflatednegativebinomial"

    /// Logarithm of the likelihood of a loglogistic distribution.
    map logLogistic(
        /// Scale parameter of the loglikelihood distribution, it is also 
        /// its median. Must be positive.
        alpha : number,
        /// Shape parameter of the loglikelihood distribution. 
        /// Must be positive.
        beta : number,
        /// Observed value. Must be positive.
        x : number) : number as "loglikelihood-loglogistic"

    /// Logarithm of the likelihood of a loglogistic distribution.
    /// If isLowerBound is true, then the x is a lower bound for 
    /// the actual observation. 
    map logLogistic(
        /// Scale parameter of the loglikelihood distribution, it is also 
        /// its median. Must be positive.
        alpha : number,
        /// Shape parameter of the loglikelihood distribution. 
        /// Must be positive.
        beta : number,
        /// Observed value. Must be positive.
        x : number,
        /// Boolean, indicates wether x is the actual value or a lower bound.
        isLowerBound : boolean) : number as "loglikelihood-loglogistic-lowerbound"
}

module pricebrk {
	/// Returns the zedfunc of the marginal purchase unit price with the merchant
	/// flavor of the price breaks where negative unit prices are possible.
	call m<Items, Break>(
		/// Product's default unit price. This value is used if there is no price
		/// break for a minimum quantity of 1; either because the price break
		/// starts at a value greater than 1, or because there is no price break
		/// at all for the product.
		Items.defPrice: number,
		/// Smallest quantity for which the price break line applies. It must
		/// be an integer greater or equal to 1. Duplicates are not allowed.
		Break.min: number,
		/// unit price for this price break line. It applies to all purchased
		/// units, not just the ones exceeding 
		Break.price: number,
		Items -> Break) : Items.zedfunc as "pricebrk.m"

	/// Returns the zedfunc of the marginal purchase unit price with the fiscal
	/// flavor of the price breaks where unit prices are positive and decreasing.
	call f<Items, Break>(
		/// Product's default unit price. This value is used if there is no price
		/// break for a minimum quantity of 1; either because the price break
		/// starts at a value greater than 1, or because there is no price break
		/// at all for the product.
		Items.defPrice: number,
		/// Smallest quantity for which the price break line applies. It must
		/// be an integer greater or equal to 1. Duplicates are not allowed.
		Break.min: number,
		/// unit price for this price break line. It applies to all purchased
		/// units, not just the ones exceeding 
		Break.price: number,
		Items -> Break) : Items.zedfunc as "pricebrk.f"
}
/// The smallest value in a group.
[preserveinput] aggregator min(a: number) : number as "min{num}"
/// The smallest value in a group.
[preserveinput] aggregator min(a: date) : date as "min{date}"
/// The smallest value in a group.
[preserveinput] aggregator min(a: text) : text as "min{text}"
/// The smallest value in an array.
[preserveinput] aggregator min(a: long) : long as "min{long}"
/// The smallest value in an array.
[preserveinput] aggregator min(a: double) : double as "min{dbl}"
/// The smallest value in an array.
[preserveinput] aggregator min(a: week) : week as "min{week}"
/// The smallest value in an array.
[preserveinput] aggregator min(a: month) : month as "min{mnth}"

/// The largest value in an array.
[preserveinput] aggregator max(a: long) : long as "max{long}"
/// The largest value in an array.
[preserveinput] aggregator max(a: double) : double as "max{dbl}"
/// The largest value in an array.
[preserveinput] aggregator max(a: week) : week as "max{week}"
/// The largest value in an array.
[preserveinput] aggregator max(a: month) : month as "max{mnth}"
/// The largest value in a group
[preserveinput] aggregator max(a: number) : number as "max{num}"
/// The largest value in a group
[preserveinput] aggregator max(a: date) : date as "max{date}"
/// The largest value in a group
[preserveinput] aggregator max(a: text) : text as "max{text}"

/// Union of sets
[preserveinput] aggregator union(a: flagset) : flagset as "union{flag}"
/// Intersection of sets
[preserveinput] aggregator intersection(a: flagset) : flagset as "intersection{flag}"

/// The nth largest value in a group
aggregator nthmax(value : number; n : number) : number as "nthmax{num;num}"

/// Return the value of `minv` having the smallest `score` in the group
aggregator argmin(score : number, minv : ranvar) : ranvar as "argmin{num,dist}"
/// Return the value of `minv` having the biggest `score` in the group
aggregator argmax(score : number, minv : ranvar) : ranvar as "argmax{num,dist}"

/// Return the value of `minv` having the smallest `score` in the group
aggregator argmin(score : number, minv : zedfunc) : zedfunc as "argmin{num,fun}"
/// Return the value of `minv` having the biggest `score` in the group
aggregator argmax(score : number, minv : zedfunc) : zedfunc as "argmax{num,fun}"

/// Return the value of `minv` having the smallest `score` in the group
aggregator argmin(score : number, minv : number) : number as "argmin{num,num}"
/// Return the value of `minv` having the biggest `score` in the group
aggregator argmax(score : number, minv : number) : number as "argmax{num,num}"

/// Return the value of `minv` having the smallest `score` in the group
aggregator argmin(score : number, minv : text) : text as "argmin{num,txt}"
/// Return the value of `minv` having the biggest `score` in the group
aggregator argmax(score : number, minv : text) : text as "argmax{num,txt}"

/// Return the value of `minv` having the smallest `score` in the group
aggregator argmin(score : number, minv : ordinal) : ordinal as "argmin{num,ord}"
/// Return the value of `minv` having the biggest `score` in the group
aggregator argmax(score : number, minv : ordinal) : ordinal as "argmax{num,ord}"

/// Return the value of `minv` having the smallest `score` in the group
aggregator argmin(score : number, minv : date) : date as "argmin{num,date}"
/// Return the value of `minv` having the biggest `score` in the group
aggregator argmax(score : number, minv : date) : date as "argmax{num,date}"

/// Return the value of `minv` having the smallest `score` in the group
aggregator argmin(score : number, minv : boolean) : boolean as "argmin{num,bool}"
/// Return the value of `minv` having the biggest `score` in the group
aggregator argmax(score : number, minv : boolean) : boolean as "argmax{num,bool}"

/// Return the value of `minv` having the smallest `score` in the group
aggregator argmin(score : text, minv : ranvar) : ranvar as "argmin{txt,dist}"
/// Return the value of `minv` having the biggest `score` in the group
aggregator argmax(score : text, minv : ranvar) : ranvar as "argmax{txt,dist}"

/// Return the value of `minv` having the smallest `score` in the group
aggregator argmin(score : text, minv : zedfunc) : zedfunc as "argmin{txt,fun}"
/// Return the value of `minv` having the biggest `score` in the group
aggregator argmax(score : text, minv : zedfunc) : zedfunc as "argmax{txt,fun}"

/// Return the value of `minv` having the smallest `score` in the group
aggregator argmin(score : text, minv : number) : number as "argmin{txt,num}"
/// Return the value of `minv` having the biggest `score` in the group
aggregator argmax(score : text, minv : number) : number as "argmax{txt,num}"

/// Return the value of `minv` having the smallest `score` in the group
aggregator argmin(score : text, minv : text) : text as "argmin{txt,txt}"
/// Return the value of `minv` having the biggest `score` in the group
aggregator argmax(score : text, minv : text) : text as "argmax{txt,txt}"

/// Return the value of `minv` having the smallest `score` in the group
aggregator argmin(score : text, minv : ordinal) : ordinal as "argmin{txt,ord}"
/// Return the value of `minv` having the biggest `score` in the group
aggregator argmax(score : text, minv : ordinal) : ordinal as "argmax{txt,ord}"

/// Return the value of `minv` having the smallest `score` in the group
aggregator argmin(score : text, minv : date) : date as "argmin{txt,date}"
/// Return the value of `minv` having the biggest `score` in the group
aggregator argmax(score : text, minv : date) : date as "argmax{txt,date}"

/// Return the value of `minv` having the smallest `score` in the group
aggregator argmin(score : text, minv : boolean) : boolean as "argmin{txt,bool}"
/// Return the value of `minv` having the biggest `score` in the group
aggregator argmax(score : text, minv : boolean) : boolean as "argmax{txt,bool}"

/// Return the value of `minv` having the smallest `date` in the group
aggregator argmin(date : date, minv : boolean) : boolean as "argmin{date,bool}"
/// Return the value of `minv` having the biggest `date` in the group
aggregator argmax(date : date, minv : boolean) : boolean as "argmax{date,bool}"

/// Return the value of `minv` having the smallest `date` in the group
aggregator argmin(date : date, minv : number) : number as "argmin{date,num}"
/// Return the value of `minv` having the biggest `date` in the group
aggregator argmax(date : date, minv : number) : number as "argmax{date,num}"

/// Return the value of `minv` having the smallest `date` in the group
aggregator argmin(date : date, minv : ordinal) : ordinal as "argmin{date,ord}"
/// Return the value of `minv` having the biggest `date` in the group
aggregator argmax(date : date, minv : ordinal) : ordinal as "argmax{date,ord}"

/// Return the value of `minv` having the smallest `date` in the group
aggregator argmin(date : date, minv : ranvar) : ranvar as "argmin{date,dist}"
/// Return the value of `minv` having the biggest `date` in the group
aggregator argmax(date : date, minv : ranvar) : ranvar as "argmax{date,dist}"

/// Return the value of `minv` having the smallest `date` in the group
aggregator argmin(date : date, minv : text) : text as "argmin{date,txt}"
/// Return the value of `minv` having the biggest `date` in the group
aggregator argmax(date : date, minv : text) : text as "argmax{date,txt}"

/// Return the value of `minv` having the smallest `date` in the group
aggregator argmin(date : date, minv : zedfunc) : zedfunc as "argmin{date,fun}"
/// Return the value of `minv` having the biggest `date` in the group
aggregator argmax(date : date, minv : zedfunc) : zedfunc as "argmax{date,fun}"

/// Return the value of `minv` having the smallest `week` in the group
aggregator argmin(week : week, minv : boolean) : boolean as "argmin{week,bool}"
/// Return the value of `minv` having the biggest `week` in the group
aggregator argmax(week : week, minv : boolean) : boolean as "argmax{week,bool}"

/// Return the value of `minv` having the smallest `week` in the group
aggregator argmin(week : week, minv : number) : number as "argmin{week,num}"
/// Return the value of `minv` having the biggest `week` in the group
aggregator argmax(week : week, minv : number) : number as "argmax{week,num}"

/// Return the value of `minv` having the smallest `week` in the group
aggregator argmin(week : week, minv : ordinal) : ordinal as "argmin{week,ord}"
/// Return the value of `minv` having the biggest `week` in the group
aggregator argmax(week : week, minv : ordinal) : ordinal as "argmax{week,ord}"

/// Return the value of `minv` having the smallest `week` in the group
aggregator argmin(week : week, minv : ranvar) : ranvar as "argmin{week,dist}"
/// Return the value of `minv` having the biggest `week` in the group
aggregator argmax(week : week, minv : ranvar) : ranvar as "argmax{week,dist}"

/// Return the value of `minv` having the smallest `week` in the group
aggregator argmin(week : week, minv : text) : text as "argmin{week,txt}"
/// Return the value of `minv` having the biggest `week` in the group
aggregator argmax(week : week, minv : text) : text as "argmax{week,txt}"

/// Return the value of `minv` having the smallest `week` in the group
aggregator argmin(week : week, minv : zedfunc) : zedfunc as "argmin{week,fun}"
/// Return the value of `minv` having the biggest `week` in the group
aggregator argmax(week : week, minv : zedfunc) : zedfunc as "argmax{week,fun}"

/// Return the value of `minv` having the smallest `month` in the group
aggregator argmin(month : month, minv : boolean) : boolean as "argmin{mnth,bool}"
/// Return the value of `minv` having the biggest `month` in the group
aggregator argmax(month : month, minv : boolean) : boolean as "argmax{mnth,bool}"

/// Return the value of `minv` having the smallest `month` in the group
aggregator argmin(month : month, minv : number) : number as "argmin{mnth,num}"
/// Return the value of `minv` having the biggest `month` in the group
aggregator argmax(month : month, minv : number) : number as "argmax{mnth,num}"

/// Return the value of `minv` having the smallest `month` in the group
aggregator argmin(month : month, minv : ordinal) : ordinal as "argmin{mnth,ord}"
/// Return the value of `minv` having the biggest `month` in the group
aggregator argmax(month : month, minv : ordinal) : ordinal as "argmax{mnth,ord}"

/// Return the value of `minv` having the smallest `month` in the group
aggregator argmin(month : month, minv : ranvar) : ranvar as "argmin{mnth,dist}"
/// Return the value of `minv` having the biggest `month` in the group
aggregator argmax(month : month, minv : ranvar) : ranvar as "argmax{mnth,dist}"

/// Return the value of `minv` having the smallest `month` in the group
aggregator argmin(month : month, minv : text) : text as "argmin{mnth,txt}"
/// Return the value of `minv` having the biggest `month` in the group
aggregator argmax(month : month, minv : text) : text as "argmax{mnth,txt}"

/// Return the value of `minv` having the smallest `month` in the group
aggregator argmin(month : month, minv : zedfunc) : zedfunc as "argmin{mnth,fun}"
/// Return the value of `minv` having the biggest `month` in the group
aggregator argmax(month : month, minv : zedfunc) : zedfunc as "argmax{mnth,fun}"

/// True if at least one value in the group is true.
aggregator any(a: boolean) : boolean as "any{bool}"

/// True if all values in the group are true.
aggregator all(a: boolean) : boolean as "all{bool}"

/// The sum of all values in a group.
aggregator sum(a: number) : number as "sum{num}"
/// The sum of all values in an array.
aggregator sum(a: long) : long as "sum{long}"
/// The sum of all values in an array.
aggregator sum(a: double) : double as "sum{dbl}"

/// Given a quantity requested, compute the quantity consumed for each element while consumed in a specific order
aggregator consume(qtyAvailable: number; qtyRequested: number) : number as "consume{number;number}"

/// Higher-performance, lower-precision sum of values in a group.
aggregator fastsum(a: number) : number as "fastsum{num}"

/// Product of all values in a group.
aggregator product(a: number) : number as "product{num}"

/// The average of all values in a group.
aggregator avg(a: number) : number as "avg{num}"
/// The average of all values in an array.
aggregator avg(a: double) : double as "avg{dbl}"
/// The average of all values in an array.
aggregator avg(a: long) : long as "avg{long}"

/// The number of true values in a group.
[preserveinput] aggregator count(a: boolean) : number as "count{bool}"

/// The first value in a sorted group.
[needsort, tuple] aggregator first(a: number) : number as "first{num}"
/// The first value in a sorted group.
[preserveinput, needsort, tuple] aggregator first(a: text) : text as "first{text}"
/// The first value in a sorted group.
[needsort, tuple] aggregator first(a: date) : date as "first{date}"
/// The first value in a sorted group.
[needsort, tuple] aggregator first(a: boolean) : boolean as "first{bool}"
/// The first value in a sorted group.
[needsort, tuple] aggregator first(a: ranvar) : ranvar as "first{dist}"
/// The first value in a sorted group.
[needsort, tuple] aggregator first(a: zedfunc) : zedfunc as "first{fun}"
/// The first value in a sorted array.
[needsort, tuple] aggregator first(a: double) : double as "first{dbl}"
/// The first value in a sorted array.
[needsort, tuple] aggregator first(a: long) : long as "first{long}"
/// The first value in a sorted array.
[needsort, tuple] aggregator first(a: ordinal) : ordinal as "first{ord}"
/// The first value in a sorted array.
[needsort, tuple] aggregator first(a: week) : week as "first{week}"
/// The first value in a sorted array.
[needsort, tuple] aggregator first(a: month) : month as "first{mnth}"
/// The first value in a sorted array.
[needsort, tuple] aggregator first(a: flagset) : flagset as "first{flag}"
/// The first value in a sorted array.
[needsort, tuple] aggregator first(a: embedding) : embedding as "first{emb}"

/// The last value in a sorted group.
[needsort, tuple] aggregator last(a: number) : number as "last{num}"
/// The last value in a sorted group.
[preserveinput, needsort, tuple] aggregator last(a: text) : text as "last{text}"
/// The last value in a sorted group.
[needsort, tuple] aggregator last(a: date) : date as "last{date}"
/// The last value in a sorted group.
[needsort, tuple] aggregator last(a: boolean) : boolean as "last{bool}"
/// The last value in a sorted group.
[needsort, tuple] aggregator last(a: ranvar) : ranvar as "last{dist}"
/// The last value in a sorted group.
[needsort, tuple] aggregator last(a: zedfunc) : zedfunc as "last{fun}"
/// The last value in a sorted array.
[needsort, tuple] aggregator last(a: double) : double as "last{dbl}"
/// The last value in a sorted array.
[needsort, tuple] aggregator last(a: long) : long as "last{long}"
/// The last value in a sorted array.
[needsort, tuple] aggregator last(a: ordinal) : ordinal as "last{ord}"
/// The last value in a sorted array.
[needsort, tuple] aggregator last(a: week) : week as "last{week}"
/// The last value in a sorted array.
[needsort, tuple] aggregator last(a: month) : month as "last{mnth}"
/// The last value in a sorted array.
[needsort, tuple] aggregator last(a: flagset) : flagset as "last{flag}"
/// The last value in a sorted array.
[needsort, tuple] aggregator last(a: embedding) : embedding as "last{emb}"

/// True on the first element, and on each element different from the previous
[needsort] aggregator changed(a: number) : boolean as "changed{num}"
/// True on the first element, and on each element different from the previous
[needsort] aggregator changed(a: text) : boolean as "changed{text}"
/// True on the first element, and on each element different from the previous
[needsort] aggregator changed(a: date) : boolean as "changed{date}"
/// True on the first element, and on each element different from the previous
[needsort] aggregator changed(a: boolean) : boolean as "changed{bool}"
/// True on the first element, and on each element different from the previous
[needsort] aggregator changed(a: ranvar) : boolean as "changed{dist}"
/// True on the first element, and on each element different from the previous
[needsort] aggregator changed(a: zedfunc) : boolean as "changed{fun}"
/// True on the first element, and on each element different from the previous
[needsort] aggregator changed(a: double) : boolean as "changed{dbl}"
/// True on the first element, and on each element different from the previous
[needsort] aggregator changed(a: long) : boolean as "changed{long}"
/// True on the first element, and on each element different from the previous
[needsort] aggregator changed(a: ordinal) : boolean as "changed{ord}"
/// True on the first element, and on each element different from the previous
[needsort] aggregator changed(a: week) : boolean as "changed{week}"
/// True on the first element, and on each element different from the previous
[needsort] aggregator changed(a: month) : boolean as "changed{mnth}"

/// A value in a group.
[noscan, tuple] aggregator whichever(a: number) : number as "first{num}"
/// A value in a group.
[noscan, preserveinput, tuple] aggregator whichever(a: text) : text as "first{text}"
/// A value in a group.
[noscan, tuple] aggregator whichever(a: date) : date as "first{date}"
/// A value in a group.
[noscan, tuple] aggregator whichever(a: boolean) : boolean as "first{bool}"
/// A value in a group.
[noscan, tuple] aggregator whichever(a: ranvar) : ranvar as "first{dist}"
/// A value in a group.
[noscan, tuple] aggregator whichever(a: zedfunc) : zedfunc as "first{fun}"
/// A value in a group.
[noscan, tuple] aggregator whichever(a: double) : double as "first{dbl}"
/// A value in a group.
[noscan, tuple] aggregator whichever(a: long) : long as "first{long}"
/// A value in a group.
[noscan, tuple] aggregator whichever(a: ordinal) : ordinal as "first{ord}"
/// A value in a group.
[noscan, tuple] aggregator whichever(a: month) : month as "first{mnth}"
/// A value in a group.
[noscan, tuple] aggregator whichever(a: week) : week as "first{week}"
/// A value in a group.
[noscan, tuple] aggregator whichever(a: flagset) : flagset as "first{flag}"
/// A value in a group.
[noscan, tuple] aggregator whichever(a: embedding) : embedding as "first{emb}"

/// The median of all values in a group.
[preserveinput, noscan] aggregator median(a: number) : number as "median{num}"
/// The median of all values in a group.
[preserveinput, noscan] aggregator median(a: date) : date as "median{date}"
/// The median of all values in a group.
[preserveinput, noscan] aggregator median(a: text) : text as "median{text}"
/// The median of all values in a group.
[preserveinput, noscan] aggregator median(a: boolean) : boolean as "median{bool}"

/// The standard deviation based on a sample
aggregator stdev(a: number) : number as "stdev{num}"
/// Standard deviation based on the entire population
aggregator stdevp(a: number) : number as "stdevp{num}"

/// The most frequent value in a group.
[preserveinput, cost(500), noscan]
aggregator mode(a: number) : number as "mode{num}"

/// The most frequent value in a group.
[preserveinput, cost(500), noscan]
aggregator mode(a: text) : text as "mode{text}"

/// The most frequent value in a group.
[preserveinput, noscan] 
aggregator mode(a: date) : date as "mode{date}"

/// The most frequent value in a group.
[preserveinput, noscan] 
aggregator mode(a: boolean) : boolean as "mode{bool}"

/// Calculate the ratio of non-zero number related to the number of
/// elements in the vector. Returns 1 on empty vector.
aggregator ratio(a: number) : number as "ratio{num}"
/// Calculate the ratio of non-empty string related to the number of
/// elements in the vector. Returns 1 on empty vector.
aggregator ratio(a: text) : number as "ratio{text}"
/// Calculate the ratio of true value related to the total number
/// of elements in the vecotr. Returns 1 on empty vector.
aggregator ratio(a: boolean) : number as "ratio{bool}"

/// The number of distinct values in a group.
[preserveinput, noscan] aggregator distinct(a: number) : number as "distinct{num}"
/// The number of distinct values in a group.
[preserveinput, noscan] aggregator distinct(a: text) : number as "distinct{text}"
/// The number of distinct values in a group.
[preserveinput, noscan] aggregator distinct(a: date) : number as "distinct{date}"
/// The number of distinct values in a group.
[preserveinput, noscan] aggregator distinct(a: boolean) : number as "distinct{bool}"
/// The number of distinct values in a group.
[preserveinput, noscan] aggregator distinct(a: ordinal) : number as "distinct{ord}"
/// The number of distinct values in a group.
[preserveinput, noscan] aggregator distinct(a: month) : number as "distinct{month}"
/// The number of distinct values in a group.
[preserveinput, noscan] aggregator distinct(a: week) : number as "distinct{week}"

/// Approximate number of distinct values in a group.
[preserveinput, noscan] aggregator distinctapprox(a: text) : number as "distinctapprox{text}"

/// Approximate number of distinct values in a group.
[preserveinput, noscan] aggregator distinctapprox(a: number) : number as "distinctapprox{num}"

/// The value in a group that contains only one distinct value.
[preserveinput, sideeffect] aggregator same(a: number) : number as "same{num}"
/// The value in a group that contains only one distinct value.
[preserveinput, sideeffect] aggregator same(a: text) : text as "same{text}"
/// The value in a group that contains only one distinct value.
[preserveinput, sideeffect] aggregator same(a: date) : date as "same{date}"
/// The value in a group that contains only one distinct value.
[preserveinput, sideeffect] aggregator same(a: boolean) : boolean as "same{bool}"
/// The value in a group that contains only one distinct value.
[preserveinput, sideeffect] aggregator same(a: ranvar) : ranvar as "same{dist}"
/// The value in a group that contains only one distinct value.
[preserveinput, sideeffect] aggregator same(a: zedfunc) : zedfunc as "same{fun}"
/// The value in a group that contains only one distinct value.
[preserveinput, sideeffect] aggregator same(a: ordinal) : ordinal as "same{ord}"
/// The value in a array that contains only one distinct value.
[preserveinput, sideeffect] aggregator same(a: double) : double as "same{dbl}"
/// The value in a array that contains only one distinct value.
[preserveinput, sideeffect] aggregator same(a: long) : long as "same{long}"
/// The value in a array that contains only one distinct value.
[preserveinput, sideeffect] aggregator same(a: week) : week as "same{week}"
/// The value in a array that contains only one distinct value.
[preserveinput, sideeffect] aggregator same(a: month) : month as "same{mnth}"
/// The value in a array that contains only one distinct value.
[preserveinput, sideeffect] aggregator same(a: flagset) : flagset as "same{flag}"
/// The value in a array that contains only one distinct value.
[preserveinput, sideeffect] aggregator same(a: embedding) : embedding as "same{emb}"


/// Assert that the size of the group is a single value
/// Fail if more than one value is present
[preserveinput, sideeffect, tuple] aggregator single(a: number) : number as "single{num}"
/// Assert that the size of the group is a single value
/// Fail if more than one value is present
[preserveinput, sideeffect, tuple] aggregator single(a: text) : text as "single{text}"
/// Assert that the size of the group is a single value
/// Fail if more than one value is present
[preserveinput, sideeffect, tuple] aggregator single(a: date) : date as "single{date}"
/// Assert that the size of the group is a single value
/// Fail if more than one value is present
[preserveinput, sideeffect, tuple] aggregator single(a: boolean) : boolean as "single{bool}"
/// Assert that the size of the group is a single value
/// Fail if more than one value is present
[preserveinput, sideeffect, tuple] aggregator single(a: ranvar) : ranvar as "single{dist}"
/// Assert that the size of the group is a single value
/// Fail if more than one value is present
[preserveinput, sideeffect, tuple] aggregator single(a: zedfunc) : zedfunc as "single{fun}"
/// The value in a array that contains only one distinct value.
[preserveinput, sideeffect, tuple] aggregator single(a: double) : double as "single{dbl}"
/// The value in a array that contains only one distinct value.
[preserveinput, sideeffect, tuple] aggregator single(a: long) : long as "single{long}"
/// The value in a array that contains only one distinct value.
[preserveinput, sideeffect, tuple] aggregator single(a: week) : week as "single{week}"
/// The value in a array that contains only one distinct value.
[preserveinput, sideeffect, tuple] aggregator single(a: month) : month as "single{mnth}"
/// The value in a array that contains only one distinct value.
[preserveinput, sideeffect, tuple] aggregator single(a: ordinal) : ordinal as "single{ord}"
/// The value in a array that contains only one distinct value.
[preserveinput, sideeffect, tuple] aggregator single(a: embedding) : embedding as "single{emb}"

/// Tell if all the values in a group are identical
aggregator areSame(a: number) : boolean as "areSame{num}"
/// Tell if all the values in a group are identical
aggregator areSame(a: text) : boolean as "areSame{text}"
/// Tell if all the values in a group are identical
aggregator areSame(a: date) : boolean as "areSame{date}"
/// Tell if all the values in a group are identical
aggregator areSame(a: boolean) : boolean as "areSame{bool}"
/// Tell if all the values in a group are identical
aggregator areSame(a: ranvar) : boolean as "areSame{dist}"
/// Tell if all the values in a group are identical
aggregator areSame(a: zedfunc) : boolean as "areSame{fun}"
/// Tell if all the values in a array are identical
aggregator areSame(a: ordinal) : boolean as "areSame{ord}"
/// Tell if all the values in a array are identical
aggregator areSame(a: double) : boolean as "areSame{dbl}"
/// Tell if all the values in a array are identical
aggregator areSame(a: long) : boolean as "areSame{long}"
/// Tell if all the values in a array are identical
aggregator areSame(a: week) : boolean as "areSame{week}"
/// Tell if all the values in a array are identical
aggregator areSame(a: month) : boolean as "areSame{mnth}"
/// Tell if all the values in a array are identical
aggregator areSame(a: embedding) : boolean as "areSame{emb}"

/// Remember and accumulate a value to be retrieved in a
/// following iteration.
[needsort]
scan sumlagged(
	/// Quantity to accumulate for a next iteration
	value : number,
	/// Index in number of iterations at which the value will be
	/// accumulated. It is expected to be in the range [0; 2048[
	///
	///   sumlagged(18, enumerate() + 1) // will add 18 to the next call
	///   sumlagged(18, enumerate()) // will add 18 to the remembered value and return it
	///   sumlagged(18, enumerate() + -1) // Error
	///
	writeOffset : number) : number as "sumlag{num,num}"

/// Partitions the data into K different buckets numbered 1..K
[needsort]
scan partition(a: number) : number as "partition{num}"

/// Ranks the value in ascending order, starting at 1
[needgroupsize, needsort]
scan rank() : number as "rank{}"

/// Ranks the value in descending order, starting at 1
[needgroupsize, needsort]
scan rankrev() : number as "rev_enumerate{}"

/// Returns true for the first value of the group according
/// to the given ordering.
[needsort]
scan argfirst() : boolean as "is_first{}"

/// Returns true for one value of the group.
scan argwhichever() : boolean as "is_first{}"

/// Returns true for one last value of the group according
/// to the given ordering.
[needgroupsize, needsort]
scan arglast() : boolean as "is_last{}"

/// Returns true for the first value of the group according
/// to the given ordering. If the group is empty due to the
/// condition, you may have only `false`.
[needsort]
scan argfirst(condition: boolean) : boolean as "is_first{bool}"

/// Returns true for a value of the group for which the 
/// condition is true.
scan argwhichever(condition: boolean) : boolean as "is_first{bool}"

/// Cumulative sum of a vector
[needsort]
scan cumsum(n: number) : number as "sum{num}"

/// Cumulative product of a vector
[needsort]
scan cumprod(n: number) : number as "product{num}"

/// Return the number of element in the current group
[needgroupsize, needsort]
scan groupsize() : number as "groupsize{}"

/// Return the 0-based increasing index of an item in a group
[needsort]
scan enumerate() : number as "enumerate{}"

/// Takes an incomplete vector of values and a Boolean vector
/// that determines where the valid values are present. It returns
/// a full vector of valid values, that has been completed by
/// spreading valid values into the non-valid ones.
[needsort]
scan smudge(values: boolean, is_present: boolean) : boolean as "smudge{bool,bool}"

/// Takes an incomplete vector of values and a Boolean vector
/// that determines where the valid values are present. It returns
/// a full vector of valid values, that has been completed by
/// spreading valid values into the non-valid ones.
[needsort]
scan smudge(values: number, is_present: boolean) : number as "smudge{num,bool}"

/// Takes an incomplete vector of values and a Boolean vector
/// that determines where the valid values are present. It returns
/// a full vector of valid values, that has been completed by
/// spreading valid values into the non-valid ones.
[needsort]
scan smudge(values: date, is_present: boolean) : date as "smudge{date,bool}"

/// Takes an incomplete vector of values and a Boolean vector
/// that determines where the valid values are present. It returns
/// a full vector of valid values, that has been completed by
/// spreading valid values into the non-valid ones.
[needsort]
scan smudge(values: text, is_present: boolean) : text as "smudge{text,bool}"

/// Returns the random variable that 'counts' the observations.
/// If `f(k)` equals to the number of times that `k` was observed
/// in the inputs. The ranvar is then normalized.
[noscan] aggregator ranvar(a: number) : ranvar as "ranvar{num}"

/// Returns the random variable that 'counts' the observations.
/// If `f(k)` equals to the number of times that `k` was observed
/// in the inputs. The ranvar is then normalized.
[noscan] aggregator ranvar(a: number, weight: number) : ranvar as "ranvar{num,num}"

/// Returns the average of a set of ranvars.
[noscan] aggregator mixture(a: ranvar) : ranvar as "mixture{dist}"

/// Returns the weighted average of a set of ranvars.
[noscan] aggregator mixture(
    /// input ranvar
    a: ranvar,
    /// weight, must be in the range [0; 1], if any weight is outside
    /// of this range, an error will be raised.
    weight: number) : ranvar as "mixture{dist,num}"

/// Returns a zedfunc empirically built from the given pairs (input, value).
/// The return zedfunc is asymptotically constant left and right, and equal
/// to the extreme input values, respectively left and right.
[noscan] aggregator zedfunc(index: number, value: number) : zedfunc as "zedfunc{num,num}"

/// The additive convolution of multiple ranvars.
[cost(1000)] aggregator sum(a: ranvar) : ranvar as "sum{ranv}"

/// The ranvar as the minimum of multiple ranvars.
[noscan] aggregator min(a: ranvar) : ranvar as "min{ranv}"

/// The ranvar as the minimum of multiple ranvars.
[noscan] aggregator max(a: ranvar) : ranvar as "max{ranv}"

/// The point-wise sum of zedfuncs.
[noscan] aggregator sum(a: zedfunc) : zedfunc as "sum{zedf}"

/// The N-th percentile of the values in a group.
[noscan] aggregator percentile(d: number; percentile: number) : number as "percentile{num,num}"
/// The N-th percentile of the values in a group.
[noscan] aggregator percentile(d: text; percentile: number) : text as "percentile{text,num}"
/// The N-th percentile of the values in a group.
[noscan] aggregator percentile(d: date; percentile: number) : date as "percentile{date,num}"
/// The N-th percentile of the values in a group.
[noscan] aggregator percentile(d: week; percentile: number) : week as "percentile{week,num}"
/// The N-th percentile of the values in a group.
[noscan] aggregator percentile(d: month; percentile: number) : month as "percentile{mnth,num}"

/// Exponential smoothing, the value returned for a line is equal to 
/// `factor * src + (1 - factor) * previous` where `previous` is the value returned 
/// for the previous line or `init` if on the first line.
[needsort] aggregator expsmooth(src: number, factor: number; init: number) : number as "expsmooth{num,num;num}"

/// returns the Shannon Entropy of the group. The value is returned expressed in shannons. 
[noscan] aggregator entropy(a: number) : number as "entropy{num}"
/// returns the Shannon Entropy of the group. The value is returned expressed in shannons. 
[noscan] aggregator entropy(a: date) : number as "entropy{date}"
/// returns the Shannon Entropy of the group. The value is returned expressed in shannons. 
[noscan] aggregator entropy(a: text) : number as "entropy{text}"
/// returns the Shannon Entropy of the group. The value is returned expressed in shannons. 
[noscan] aggregator entropy(a: boolean) : number as "entropy{bool}"

/// Join text together using a specified separator.
/// Example:
/// with `Items.Id` with values: `"A"`, `"Be"`, `"C"` and `"D"`
///    `join(Id, ":") == "A:Be:C:D"`
[needsort]
aggregator join(
    /// Text to be joined together
    what : text;
    /// Separator used between all the elements:
    separator: text) : text as "join{txt;txt}"

/// Join text together without separator.
[needsort] aggregator concat(a: text) : text as "concat{txt}"

/// The reverse of the cumulative sum, returning a ZFunc. Values must be non-negative.
[needsort] aggregator cuminv(a: number) : zedfunc as "cuminv"
##########################################################################
##  Operators
##########################################################################


[const] map (+)(left: number, right: number) : number as "add(num,num)"
[const] map (+)(left: number, right: date) : date as "add(num,date)"
[const] map (+)(left: date, right: number) : date as "add(date,num)"
map (+)(left: zedfunc, right: zedfunc) : zedfunc as "add(fun,fun)"
map (+)(left: zedfunc, right: number) : zedfunc as "add(fun,num)"
map (+)(left: number, right: zedfunc) : zedfunc as "add(num,fun)"
[const] map (+)(left: double, right: double) : double as "add(dbl,dbl)"
[const] map (+)(left: long, right: long) : long as "add(long,long)"
[const] map (+)(left: week, right: number) : week as "add(week,num)"
[const] map (+)(left: number, right: week) : week as "add(num,week)"
[const] map (+)(left: month, right: number) : month as "add(mnth,num)"
[const] map (+)(left: number, right: month) : month as "add(num,mnth)"

[const] map (+)(a: number) : number as "identity(num)"
[const] map (+)(a: date) : number as "to-num"


[const] map (-)(a: number) : number as "minus(num)"
[const] map (-)(a: date) : number as "minus(date)"
map (-)(a: ranvar) : ranvar as "minus(ranv)"
map (-)(a: zedfunc) : zedfunc as "minus(fun)"
[const] map (-)(a: double) : double as "minus(dbl)"
[const] map (-)(a: long) : long as "minus(long)"

[const] map (-)(left: double, right: double) : double as "sub(dbl,dbl)"
[const] map (-)(left: long, right: long) : long as "sub(long,long)"
[const] map (-)(left: week, right: week) : number as "sub(week,week)"
[const] map (-)(left: week, right: number) : week as "sub(week,num)"
[const] map (-)(left: month, right: month) : number as "sub(mnth,mnth)"
[const] map (-)(left: month, right: number) : month as "sub(mnth,num)"
[const] map (-)(left: number, right: number) : number as "sub(num,num)"
[const] map (-)(left: date, right: number) : date as "sub(date,num)"
[const] map (-)(left: date, right: date) : number as "sub(date,date)"
map (-)(left: zedfunc, right: zedfunc) : zedfunc as "sub(fun,fun)"
map (-)(left: zedfunc, right: number) : zedfunc as "sub(fun,num)"
map (-)(left: number, right: zedfunc) : zedfunc as "sub(num,fun)"


[const] map (*)(left: number, right: number) : number as "mul(num,num)"
map (*)(left: zedfunc, right: zedfunc) : zedfunc as "mul(fun,fun)"
map (*)(left: zedfunc, right: number) : zedfunc as "mul(fun,num)"
map (*)(left: number, right: zedfunc) : zedfunc as "mul(num,fun)"
map (*)(left: text, right: number) : text as "mul(txt,num)"
[const] map (*)(left: double, right: double) : double as "mul(dbl,dbl)"
[const] map (*)(left: long, right: long) : long as "mul(long,long)"


[sideeffect, const] map (^)(left: number, right: number) : number as "pow(num,num)"

[const] map (~)(a: text) : text as "uppercase"

[cost(1000)]
map (+)(left: ranvar, right: ranvar) : ranvar as "add(ranv,ranv)"
map (+)(left: ranvar, right: number) : ranvar as "add(ranv,num)"
map (+)(left: number, right: ranvar) : ranvar as "add(num,ranv)"

[cost(1000)]
map (-)(left: ranvar, right: ranvar) : ranvar as "sub(ranv,ranv)"
map (-)(left: ranvar, right: number) : ranvar as "sub(ranv,num)"
map (-)(left: number, right: ranvar) : ranvar as "sub(num,ranv)"

[sideeffect, cost(1000)] map (^)(left: ranvar, right: ranvar) : ranvar as "pow(ranv,ranv)"
[sideeffect, cost(1000)] map (^)(left: ranvar, right: number) : ranvar as "pow(ranv,num)"

[cost(1000)]
map (*)(left: ranvar, right: ranvar) : ranvar as "mul(ranv,ranv)"
map (*)(left: ranvar, right: number) : ranvar as "mul(ranv,num)"
map (*)(left: number, right: ranvar) : ranvar as "mul(num,ranv)"

[const] map (/.)(left: number, right: number) : number as "sdiv(num,num)"

[sideeffect, const] map (/)(left: number, right: number) : number as "div(num,num)"
[sideeffect, const] map (/)(left: date, right: number) : number as "div(date,num)"
[sideeffect, const] map (/)(left: number, right: date) : number as "div(num,date)"
[sideeffect] map (/)(left: zedfunc, right: number) : zedfunc as "div(fun,num)"
[sideeffect, const] map (/)(left: double, right: double) : double as "div(dbl,dbl)"
[sideeffect, const] map (/)(left: long, right: long) : long as "div(long,long)"

[const] map ("mod")(left: number, right: number) : number as "mod(num,num)"
[const] map ("mod")(left: date, right: number) : number as "mod(date,num)"

[const] map (==)(left: number, right: number) : boolean as "eq(num,num)"
[const] map (==)(left: date, right: date) : boolean as "eq(date,date)"
[const] map (==)(left: date, right: number) : boolean as "eq(date,num)"
[const] map (==)(left: number, right: date) : boolean as "eq(num,date)"
[const] map (==)(left: boolean, right: boolean) : boolean as "eq(bool,bool)"
[const] map (==)(left: text, right: text) : boolean as "eq(text,text)"
map (==)(left: ranvar, right: ranvar) : boolean as "eq(dist,dist)"
map (==)(left: zedfunc, right: zedfunc) : boolean as "eq(fun,fun)"
[const] map (==)(left: double, right: double) : boolean as "eq(dbl,dbl)"
[const] map (==)(left: long, right: long) : boolean as "eq(long,long)"
[const] map (==)(left: flagset, right: flagset) : boolean as "eq(flag,flag)"
[const] map (==)(left: ordinal, right: ordinal) : boolean as "eq(ord,ord)"
[const] map (==)(left: week, right: week) : boolean as "eq(week,week)"
[const] map (==)(left: month, right: month) : boolean as "eq(mnth,mnth)"


[const] map (~~)(left: text, right: text) : boolean as "sim(text,text)"

[const] map (!~)(left: text, right: text) : boolean as "nsim(text,text)"

[const] map (!=)(left: number, right: number) : boolean as "neq(num,num)"
[const] map (!=)(left: date, right: date) : boolean as "neq(date,date)"
[const] map (!=)(left: date, right: number) : boolean as "neq(date,num)"
[const] map (!=)(left: number, right: date) : boolean as "neq(num,date)"
[const] map (!=)(left: boolean, right: boolean) : boolean as "neq(bool,bool)"
[const] map (!=)(left: text, right: text) : boolean as "neq(text,text)"
map (!=)(left: ranvar, right: ranvar) : boolean as "neq(dist,dist)"
map (!=)(left: zedfunc, right: zedfunc) : boolean as "neq(fun,fun)"
[const] map (!=)(left: double, right: double) : boolean as "neq(dbl,dbl)"
[const] map (!=)(left: long, right: long) : boolean as "neq(long,long)"
[const] map (!=)(left: flagset, right: flagset) : boolean as "neq(flag,flag)"
[const] map (!=)(left: ordinal, right: ordinal) : boolean as "neq(ord,ord)"
[const] map (!=)(left: week, right: week) : boolean as "neq(week,week)"
[const] map (!=)(left: month, right: month) : boolean as "neq(mnth,mnth)"

[const] map (<)(left: number, right: number) : boolean as "lt(num,num)"
[const] map (<)(left: date, right: date) : boolean as "lt(date,date)"
[const] map (<)(left: date, right: number) : boolean as "lt(date,num)"
[const] map (<)(left: number, right: date) : boolean as "lt(num,date)"
[const] map (<)(left: text, right: text) : boolean as "lt(text,text)"
[const] map (<)(left: double, right: double) : boolean as "lt(dbl,dbl)"
[const] map (<)(left: long, right: long) : boolean as "lt(long,long)"
map (<)(left: ordinal, right: ordinal) : boolean as "lt(ord,ord)"
[const] map (<)(left: week, right: week) : boolean as "lt(week,week)"
[const] map (<)(left: month, right: month) : boolean as "lt(mnth,mnth)"

[const] map (<=)(left: number, right: number) : boolean as "leq(num,num)"
[const] map (<=)(left: date, right: date) : boolean as "leq(date,date)"
[const] map (<=)(left: date, right: number) : boolean as "leq(date,num)"
[const] map (<=)(left: number, right: date) : boolean as "leq(num,date)"
[const] map (<=)(left: text, right: text) : boolean as "leq(text,text)"
[const] map (<=)(left: double, right: double) : boolean as "leq(dbl,dbl)"
[const] map (<=)(left: long, right: long) : boolean as "leq(long,long)"
map (<=)(left: ordinal, right: ordinal) : boolean as "leq(ord,ord)"
[const] map (<=)(left: week, right: week) : boolean as "leq(week,week)"
[const] map (<=)(left: month, right: month) : boolean as "leq(mnth,mnth)"


[const] map (>)(left: number, right: number) : boolean as "gt(num,num)"
[const] map (>)(left: date, right: date) : boolean as "gt(date,date)"
[const] map (>)(left: date, right: number) : boolean as "gt(date,num)"
[const] map (>)(left: number, right: date) : boolean as "gt(num,date)"
[const] map (>)(left: text, right: text) : boolean as "gt(text,text)"
[const] map (>)(left: double, right: double) : boolean as "gt(dbl,dbl)"
[const] map (>)(left: long, right: long) : boolean as "gt(long,long)"
map (>)(left: ordinal, right: ordinal) : boolean as "gt(ord,ord)"
[const] map (>)(left: week, right: week) : boolean as "gt(week,week)"
[const] map (>)(left: month, right: month) : boolean as "gt(mnth,mnth)"


[const] map (>=)(left: number, right: number) : boolean as "geq(num,num)"
[const] map (>=)(left: date, right: date) : boolean as "geq(date,date)"
[const] map (>=)(left: date, right: number) : boolean as "geq(date,num)"
[const] map (>=)(left: number, right: date) : boolean as "geq(num,date)"
[const] map (>=)(left: text, right: text) : boolean as "geq(text,text)"
[const] map (>=)(left: double, right: double) : boolean as "geq(dbl,dbl)"
[const] map (>=)(left: long, right: long) : boolean as "geq(long,long)"
map (>=)(left: ordinal, right: ordinal) : boolean as "geq(ord,ord)"
[const] map (>=)(left: week, right: week) : boolean as "geq(week,week)"
[const] map (>=)(left: month, right: month) : boolean as "geq(mnth,mnth)"

[const] map ("and")(left: boolean, right: boolean) : boolean as "and"
[const] map ("or")(left: boolean, right: boolean) : boolean as "or"

##########################################################################
##  Map Functions
##########################################################################


/// gradControl (temporary)
[const] map gradControl(x: number, y: number) : number as "gradControl"

/// The 1-53 ISO week number.
[const] map weekNum(d: date) : number as "weekNum"

/// The 1-12 month number.
[const] map monthNum(a: date) : number as "monthNum"

/// The year number
[const] map year(a: date) : number as "year-of-day"

/// The ISO year number
/// All days of a given week belong to the same ISO year
[const] map yearISO(a: date): number as "iso-year-of-day"

/// The first day of a year.
[const] map yearStart(a: date) : date as "year-start-of-day"

/// The first day of a year defined by a year number.
[const] map yearStart(a: number) : date as "yearStart(num)"

/// The first day of a year defined by a month primitive type.
[const] map yearStart(a: month) : date as "yearStart(mnth)"

/// The last day of a year.
[const] map yearEnd(a: date) : date as "year-end-of-day"

/// The last day of a year defined by a year number.
[const] map yearEnd(a: number) : date as "yearEnd(num)"

/// The last day of a year defined by a month primitive type.
[const] map yearEnd(a: month) : date as "yearEnd(mnth)"

/// The chinese year number
[const] map yearChinese(a: date) : number as "yearChinese"

/// The first day of a chinese year.
[const] map yearStartChinese(a: date) : date as "yearStartChinese"

/// The last day of a chinese year.
[const] map yearEndChinese(a: date) : date as "yearEndChinese"

/// The last monday before or on a day.
[const] map monday(a: date) : date as "monday"

/// The first day of the month containing a day
/// Equivalent to `date(year(a), monthNum(a), 1)` 
[const] map monthStart(a: date) : date as "month-start-of-day"

/// The last day of the month containing a day 
[const] map monthEnd(a: date) : date as "month-end-of-day"

/// The 1-31 day-of-month number
[const] map dayNum(a: date) : number as "day"

/// The day of the week, 1 is monday, 7 is sunday
map weekDayNum(a: date) : number as "weekDayNum"

/// The date on which the script is executed, offseted by a number of hours
[const] map today(
    /// Number of hours to offset the date
    hours: number) : date as "today(num)"

/// The date on which the script is executed
[const] map today() : date as "today()"

/// Parse a text containing a date, using a description format
[sideeffect] map parseDate(
    /// Text representing the date
    raw: text,
    /// Date format pattern
    format: text) : date as "parseDate(text,text)"

/// Parses text containing a number
/// example `parseNumber(Anum, ".", "'")`
[sideeffect] map parseNumber(
    /// Text to be parsed
    raw: text,
    /// Single character string specifying the thousand
    /// number separator value, like  `"'"` or `"_"`
    thousandSeparator: text,
    /// Single character string specifying the decimal
    /// separation value, like  `"."` or `","`
    decimalSeparator: text) : number as "parseNumber(txt,txt,txt)"

/// Converts a time of the day into a fraction between 0 and 1,
/// representing a fractional day.  
[sideeffect] map parseTime(
    /// Text parsed using the format "yyyy-MM-dd HH:mm:ss"
    raw: text) : number as "parseTime(text)"

/// Converts a time of the day into a fraction between 0 and 1,
/// representing a fractional day.  
[sideeffect] map parseTime(
    /// Text with time to parse.
    raw: text,
    /// Time format using a `.NET custom time format`,
    /// example `yyyy-MM-dd HH:mm:ss`
    formats: text) : number as "parseTime(text,text)"

/// Mixture of two ranvars and a weight
/// returns `p * d1 + (1 - p) * d2`
[sideeffect] map mixture(
    d1: ranvar,
    p: number,
    d2: ranvar) : ranvar as "mixture(dist,num,dist)"

/// Mixture of three ranvars and two weights
/// returns `p1 * d1 + p2 * d2 + (1 - (p1 + p2)) * d3`
[sideeffect] map mixture(
    d1: ranvar,
    p1: number,
    d2: ranvar,
    p2: number,
    d3: ranvar) : ranvar as "mixture(dist,num,dist,num,dist)"

/// Mixture of four ranvars and three weights
/// returns `p1 * d1 + p2 * d2 + p3 * d3 + (1 - (p1 + p2 + p3)) * d4`
[sideeffect] map mixture(
    d1: ranvar,
    p1: number,
    d2: ranvar,
    p2: number,
    d3: ranvar,
    p3: number,
    d4: ranvar) : ranvar as "mixture(dist,num,dist,num,dist,num,dist)"

/// Mixture of weighted Poisson ranvars generated from the input ranvar.
[cost(1000), sideeffect]
map smooth(d: ranvar) : ranvar as "smooth"

/// The ranvar that represents the minimum of two independent ranvars.
[sideeffect] map min(a: ranvar, b: number) : ranvar as "min(ranv,num)"
/// The ranvar that represents the minimum of two independent ranvars.
[sideeffect] map min(a: number, b: ranvar) : ranvar as "min(num,ranv)"
/// The ranvar that represents the minimum of two independent ranvars.
[sideeffect] map min(a: ranvar, b: ranvar) : ranvar as "min(ranv,ranv)"

/// The ranvar that represents the maximum of two independent ranvars.
[sideeffect] map max(a: ranvar, b: number) : ranvar as "max(ranv,num)"
/// The ranvar that represents the maximum of two independent ranvars.
[sideeffect] map max(a: number, b: ranvar) : ranvar as "max(num,ranv)"
/// The ranvar that represents the maximum of two independent ranvars.
[sideeffect] map max(a: ranvar, b: ranvar) : ranvar as "max(ranv,ranv)"

/// The zedfunc that represents the point-wise minimum of two zedfuncs.
[sideeffect] map min(a: zedfunc, b: number) : zedfunc as "min(zedf,num)"
/// The zedfunc that represents the point-wise minimum of two zedfuncs.
[sideeffect] map min(a: number, b: zedfunc) : zedfunc as "min(num,zedf)"
/// The zedfunc that represents the point-wise minimum of two zedfuncs.
[sideeffect] map min(a: zedfunc, b: zedfunc) : zedfunc as "min(zedf,zedf)"

/// The zedfunc that represents the point-wise maximum of two zedfuncs.
[sideeffect] map max(a: zedfunc, b: number) : zedfunc as "max(zedf,num)"
/// The zedfunc that represents the point-wise maximum of two zedfuncs.
[sideeffect] map max(a: number, b: zedfunc) : zedfunc as "max(num,zedf)"
/// The zedfunc that represents the point-wise maximum of two zedfuncs.
[sideeffect] map max(a: zedfunc, b: zedfunc) : zedfunc as "max(zedf,zedf)"

/// _(zero on zero)_ returns the zedfunc where
/// `k -> f(k) if k != 0 else 0`
map zoz(a: zedfunc) : zedfunc as "zoz(fun)"

/// Returns a ranvar without values outside [min, max]
[sideeffect] map truncate(
    /// ranvar to truncate
    distrib: ranvar,
    /// Lower value
    min: number,
    /// Maximum value
    max: number) : ranvar as "truncate"

/// Returns the marginal fill rate
[sideeffect] map fillrate(a: ranvar) : ranvar as "fillrate"

/// The cumulative distribution function of a ranvar returned as a zedfunc.
[sideeffect] map cdf(r: ranvar) : zedfunc as "cdf"

/// Text representation of a ranvar
map spark(a: ranvar) : text as "spark(dist)"
/// Text representation of a zedfunc
map spark(a: zedfunc) : text as "spark(fun)"
/// Text representation of an embedding
map spark(a: embedding) : text as "spark(emb)"

/// Mean of a ranvar
map mean(a: ranvar) : number as "mean(dist)"

/// Variance of ranvar
map variance(a: ranvar) : number as "variance(dist)"

/// Weighted sum of the zedfunc over Z against the ranvar probabilities
map mean(a: ranvar, b: zedfunc) : number as "mean(dist,fun)"

/// The ranvar defined by `P[X = arg] = 1`.
[sideeffect] map dirac(a: number) : ranvar as "dirac"

/// The zedfunc defined by `f: arg -> 1, _ -> 0`. 
[sideeffect] map diracz(a: number) : zedfunc as "dirac.z"

/// Returns the constant zedfunc `f(k) = a`.
map constant(a: number) : zedfunc as "constant(num)"

/// Returns the zedfunc `f(k) = a * k`.
map linear(a: number) : zedfunc as "linear(num)"

/// Returns the zedfunc `unif:k→1` but limited to the segment `[0,n]` and 0 elsewhere. 
[sideeffect] map uniform(
    /// Higher bound of the uniform function
    n: number) : zedfunc as "uniform(num)"

/// Returns the zedfunc `unif:k→1` but limited to the segment `[m,n]` and `0` elsewhere.
/// If `m−1=n`, the `uniform(m,n)` returns a zero ranvar. 
[sideeffect] map uniform(
    /// Lower bound of the uniform function
    m: number,
    /// Higher bound of the uniform function
    n: number) : zedfunc as "uniform(num,num)"

module uniform {
    
    /// Returns the zedfunc `unif:k→1` but limited to the segment `]-infinity, n]` and 0 elsewhere.
    [sideeffect] map left(
        /// Inclusive lower boundary of the segment.
        n: number) : zedfunc as "uniform.left"

    /// Returns the zedfunc `unif:k→1` but limited to the segment `[n, +infinity[` and 0 elsewhere.
    [sideeffect] map right(
        /// Inclusive upper boundary of the segment.
        n: number) : zedfunc as "uniform.right"
}

/// Returns the value of the zedfunc at `n`. If `n` is a fractional number then, the value
/// is interpolated.
map valueAt(fun : zedfunc, n: number) : number as "valueAt(fun,num)"

/// Returns the gradient of the zedfunc at `n`.
[private] map valueGradAt(fun : zedfunc, n: number, ybar: number) : number as "valueGradAt(fun,num,num)"

/// The Poisson distribution of the provided parameter.
[sideeffect] map poisson(lambda: number) : ranvar as "poisson"

/// The Negative Binomial distribution of provided mean and dispersion.
[sideeffect] map negativeBinomial(mu: number, dispersion: number) : ranvar as "negativeBinomial(num, num)"

/// The Negative Binomial distribution of provided mean and dispersion, with an inflated 
/// probability in zero.
[sideeffect] map negativeBinomial(
    /// Mean of the negative binomial component of the probability 
    /// distribution. It should be positive.
    mu: number, 
    /// Dispersion of the negative binomial component of the probability
    /// distribution. It should be greater than one.
    dispersion: number, 
    /// Weight of the dirac in zero in the probability distribution.
    /// It should be in the range [0, 1].
    zeroInflation: number) : ranvar as "negativeBinomial(num, num, num)"

/// The normal distribution of provided mean and standard deviation.
[sideeffect] map normal(mu: number, sigma: number) : ranvar as "normal(num,num)"

/// The exponential distribution of the provided parameter.
[sideeffect] map exponential(n: number) : ranvar as "exponential"

/// The loglogistic distribution of provided median and shape parameters.
[sideeffect] map logLogistic(alpha: number, beta: number) : ranvar as "logLogistic"

/// A specific quantile in a ranvar.
[sideeffect] map quantile(
    /// ranvar to inspect
    dist: ranvar,
    /// Quantile parameter
    quantile: number) : number as "quantile"

/// Calculate the Continuous Ranked Probability Score
map crps(a: ranvar, b: number) : number as "crps(dist,num)"

/// Returns the half of the energy distance which can be understood
/// as a generalization of the CRPS to a pair of ranvars. 
map crps(a: ranvar, b: ranvar) : number as "crps(dist,dist)"

/// Returns a ranvar that approximates through interpolation `k→f(k/a)`.
map transform(d: ranvar, a: number) : ranvar as "transform(dist,num)"

/// The integral of a ranvar on [a, b[    
[sideeffect] map int(
    /// ranvar to integrate
    dist: ranvar,
    /// Lower integration bound
    a: number,
    /// Higher integration bound
    b: number) : number as "int(dist,num,num)"

/// The integral of a ranvar on ]-inf, b[    
[sideeffect] map intLeft(
    /// ranvar to integrate
    dist: ranvar,
    /// Higher integration bound
    b: number) : number as "intLeft(dist,num)"

/// The integral of a ranvar on [a, inf[    
[sideeffect] map intRight(
    /// ranvar to integrate
    dist: ranvar,
    /// Lower integration bound
    a: number) : number as "intRight(dist,num)"

/// The integral of a zedfunc on [a, b[    
[sideeffect] map int(
    /// zedfunc to integrate
    fun: zedfunc,
    /// Lower integration bound
    a: number,
    /// Higher integration bound
    b: number) : number as "int(fun,num,num)"

/// Shift the zedfunc to the right (positive offset) or to the left (negative offset)
/// Transform `f` into `x -> f(x - offset)`.
[sideeffect] map shift(left: zedfunc, offset: number) : zedfunc as "shr(zedf,num)"

/// Currency conversion on a given day
[sideeffect] map forex(
    amount: number,
    fromCurrency: text,
    toCurrency: text,
    date: date) : number as "forex"

/// The last day for which a given foreign exchange rate is available
/// Beyond this day, no rates are available and calling `forex` may cause errors.
/// Both currencies must be supported.
[sideeffect] map lastForex(fromCurrency: text, toCurrency: text) : date as "lastForex"

/// Is the value a supported currency name ?
map iscurrency(currency: text) : boolean as "iscurrency"

/// The exponential of a number
[const] map exp(a: number) : number as "exp"

/// The hyperbolic tangent of a number
[const] map tanh(a: number) : number as "tanh"

/// The absolute value of a number
[const] map abs(a: number) : number as "abs"

/// The point-wise absolute value of a double
[const] map abs(a: double) : double as "abs(dbl)"

/// The point-wise absolute value of a double
[const] map abs(a: long) : long as "abs(long)"

/// The highest integer below a number.
[const] map floor(a: number) : number as "floor"

/// The lowest integer above a number
[const] map ceiling(a: number) : number as "ceiling"

/// The square root of a number
[const] map sqrt(num: number) : number as "sqrt"

/// Round a number to the nearest integer
///
/// Uses the `nearest even number` rule for midpoint number:
///
///  * `round(0.5) == 0`
///  * `round(1.5) == 2`
///
[const] map round(num: number) : number as "round"

/// The sin of a number
[const] map sin(num: number) : number as "sin"

/// The cos of a number
[const] map cos(num: number) : number as "cos"

/// Round a number to the nearest integer
///
/// Uses the `away from zero` rule for midpoint number:
///
///  * `roundNext(0.5) == 1`
///  * `roundNext(1.5) == 2`
///
[const] map roundNext(num: number) : number as "round-away-from-zero(num)"

/// Find a good rounding value for a number.
/// `arground(N)` returns the smallest value `0 <= R <= 15` such that 
/// `round(N,R) != 0` and `round(N,R) == round(N,R+2)`. The second 
/// equality is guaranteed to happen for `R == 15`.
/// `arground(0)` returns 0.
map arground(num: number) : number as "arground"

/// Round to a given number of digits
[const] map round(num: number, digits: number) : number as "round-digits"

/// Find a good rounding value for a number.
/// `arground(N,P)` returns the smallest value `0 <= R <= 15` such that 
/// `round(N,R) != 0` and `round(N,R) == round(N,R+P)`. The second 
/// equality is guaranteed to happen for `R == 15`.
/// `arground(0,P)` returns 0.
map arground(num: number, prec: number) : number as "arground-digits"

/// The logarithm of a number in base e.
[sideeffect, const] map log(a: number) : number as "log(num)"

[sideeffect, const] map ratio(a: number, b: number) : number as "ratio(num,num)"

/// A date by year month day
/// Will emit a warning if any of the numbers yield an invalid date. When
/// a date is invalid, `date(2001, 01, 01)` will be returned.
[const] map ("date")(
    /// A year number between 2001 and 2180
    years: number,
    /// A month number between 1 and 12
    months: number,
    /// A day number between 1 and 31
    days: number) : date as "date(num,num,num)"

/// Predicate telling if the number passed as argument is
/// a floating point `NaN` (not a number)
map isnan(n : number) : boolean as "isnan(num)"

/// Predicate telling if the number passed as argument is
/// a floating point `NaN` (not a number)
map isnan(n : double) : boolean as "isnan(dbl)"

/// Logarithm of the Gamma function.
map logGamma(x : number) : number as "logGamma"

/// Builtin regularization term for autodiff blocks.
/// Used in order to properly  scale the regularization term's impact on gradient 
map regularizationTerm(penality: number, loss : number, alpha : number) : number as "regularizationTerm"

/// Dispersion (ratio between variance and mean) of a distribution.
map dispersion(r : ranvar) : number as "dispersion"

/// Create a set with a single flag. Must be in 0..63
[sideeffect] map flag(n: number) : flagset as "flag(num)"

/// The number of elements in a set
map popCount(set: flagset): number as "popCount(flag)"

/// The union of two or more sets
map union(a: flagset, b: flagset): flagset as "union(flag,flag)"

/// The intersection of two or more sets
map intersection(a: flagset, b: flagset): flagset as "intersection(flag,flag)"

/// The complement of a set
map complement(set: flagset): flagset as "complement(flag)"

/// The empty set
[const] map emptySet(): flagset as "emptySet"

/// True if A is a subset of B
map isSubsetOf(setA: flagset, setB: flagset): boolean as "isSubsetOf(flag,flag)"

/// True if value 'needle' in 0..63 is present in set 'haystack'
map contains(haystack: flagset, needle: number): boolean as "contains(flag,num)"

#########################################################################
### Min/Max
#########################################################################

# variadic versions
###################

/// The smallest value in a group
[ordered] map min(a: text, *) : text as "min(txt)"
/// The smallest value in a group
[ordered] map min(a: date, *) : date as "min(date)"
/// The smallest value in a group
[ordered] map min(a: number, *) : number as "min(num)"

/// The largest value in a group
[ordered] map max(a: text, *) : text as "max(txt)"
/// The largest value in a group
[ordered] map max(a: date, *) : date as "max(date)"
/// The largest value in a group
[ordered] map max(a: number, *) : number as "max(num)"

# Binary versions
#################

/// The smallest value in a group
[const] map min(a : number, b : number) : number as "min(num,num)"
/// The smallest value in a group
[const] map min(a : date, b : date) : date as "min(date,date)"
/// The smallest value in a group
[const] map min(a : text, b : text) : text as "min(txt,txt)"
/// The smallest value in a group
[const] map min(a : long, b : long) : long as "min(long,long)"
/// The smallest value in a group
[const] map min(a : double, b : double) : double as "min(dbl,dbl)"
/// The smallest value in a group
[const] map min(a : week, b : week) : week as "min(week,week)"
/// The smallest value in a group
[const] map min(a : month, b : month) : month as "min(mnth,mnth)"

/// The largest value in a group
[const] map max(a : number, b : number) : number as "max(num,num)"
/// The largest value in a group
[const] map max(a : date, b : date) : date as "max(date,date)"
/// The largest value in a group
[const] map max(a : text, b : text) : text as "max(txt,txt)"
/// The largest value in a group
[const] map max(a : long, b : long) : long as "max(long,long)"
/// The largest value in a group
[const] map max(a : double, b : double) : double as "max(dbl,dbl)"
/// The largest value in a group
[const] map max(a : week, b : week) : week as "max(week,week)"
/// The largest value in a group
[const] map max(a : month, b : month) : month as "max(mnth,mnth)"

/// The first argument. Special use in autodiff
map noGrad(a : number) : number as "noGrad(num)"

/// Month from year and month-number
[const] map month(year: number, month: number) : month as "month(num,num)"

/// Week from isoYear and week-number
[const] map week(isoYear: number, week: number) : week as "week(num,num)"

/// Date within a month
[const] map date(month: month, day: number) : date as "date(mnth,num)"

/// Date with a week
[const] map date(week: week, day: number) : date as "date(week,num)"

/// The first day of a month
[const] map monthStart(month: month) : date as "monthStart(mnth)"

/// The last day of a month
map monthEnd(month: month) : date as "monthEnd(mnth)"

/// The ISO year of a week
map yearISO(week: week) : number as "yearISO(week)"

/// The ISO week number of a week
[const] map weekNum(week: week) : number as "weekNum(week)"

/// The number of a month, 1-12
map monthNum(month: month) : number as "monthNum(mnth)"

/// The year of a month
map year(month: month) : number as "year(mnth)"

/// The monday of a week
[const] map monday(week: week) : date as "monday(week)"

/// The month to which a date belongs
[const] map month(date: date) : month as "month"

/// The week to which a date belongs
[const] map week(date: date) : week as "week"

/// The year quarter to which a date belongs, 1-4
[const] map quarter(date: date) : number as "quarter"

/// Returns whether a text can be parsed as a week with the specified format. If it can, also returns the parsed week.
map tryParseWeek(
    /// Text representing the week
    raw: text,
    /// Week format pattern
    format: text) : (boolean, week) as "tryParseWeek(text,text)"

/// Returns whether a text can be parsed as a date with the specified format. If it can, also returns the parsed date.
map tryParseDate(
    /// Text representing the date
    raw: text,
    /// Date format pattern
    format: text) : (boolean, date) as "tryParseDate(text,text)"

/// Returns whether a text can be parsed as a number with the specified format. If it can, also returns the parsed number.
/// example `tryParseNumber(Anum, ".", "'")`
map tryParseNumber(
    /// Text to be parsed
    raw: text,
    /// Single character string specifying the thousand
    /// number separator value, like  `"'"` or `"_"`
    thousandSeparator: text,
    /// Single character string specifying the decimal
    /// separation value, like  `"."` or `","`
    decimalSeparator: text) : (boolean, number) as "tryParseNumber(text,text,text)"
    
/// Returns whether a text can be parsed as a time with the specified format. If it can, also returns the parsed time.
map tryParseTime(
    /// Text representing the time
    raw: text,
    /// Datetime format pattern
    format: text) : (boolean, number) as "tryParseTime(text,text)"

/// Convert an inspector name search key 
/// into an url to be used as a link to a specific
/// slice in current project dashboard
[sideeffect, restricted]
map sliceSearchUrl(sliceSearch: text) : text as "slicesearchurl(txt)"

/// Convert a project id and an inspector name search
/// key into an url to be used as a link to a specific
/// dashboard slice
[const, sideeffect, restricted]
map sliceSearchUrl(project: number, sliceSearch: text) : text as "slicesearchurl(num,txt)"

/// Convert an inspector name search
/// key and a tab name into an url to be used as a link to a specific
/// dashboard slice and tab in the current project dashboard
[sideeffect, restricted]
map sliceSearchUrl(sliceSearch: text, tabSearch: text) : text as "slicesearchurl(txt,txt)"

/// Convert a project id, an inspector name search
/// key and a tab name into an url to be used as a link to a specific
/// dashboard slice and tab
[const, sideeffect, restricted]
map sliceSearchUrl(project: number, sliceSearch: text, tabSearch: text) : text as "slicesearchurl(num,txt,txt)"

/// Produces a link to the specified slice, in the current dashboard
[restricted]
map sliceUrl(slice: ordinal) : text as "sliceurl(ord)"

/// Produces a link to the specified slice & tab, in the current dashboard
[restricted]
map sliceUrl(slice: ordinal, tab: text) : text as "sliceurl(ord,txt)"

/// Produces an url towards the current project dashboard
[restricted]
map dashUrl() : text as "dashurl()"

/// Convert a tab name into an url 
/// to be used as a link to a specific
/// dashboard tab in current project dashboard
[sideeffect, restricted]
map dashUrl(tabSearch: text) : text as "dashurl(txt)"

/// Convert a project id and a tab name
/// into an url to be used as a link to a specific
/// dashboard tab
[const, sideeffect, restricted]
map dashUrl(project: number, tabSearch: text) : text as "dashurl(num,txt)"

/// Convert a project id into an url
/// to be used as a link to a dashboard
[const, sideeffect, restricted]
map dashUrl(project: number) : text as "dashurl(num)"

/// Produces an url towards the current run's
/// dashboard.
[restricted]
map currentDashUrl() : text as "currentdashurl()"

/// Returns a download url for the given file in /files, remembers that said file is referenced by this script.
[const, restricted]
map downloadUrl(fullpath: text) : text as "downloadurl(txt)"

/// Returns a download URL for the file with the provided hash, to be downloaded as the provided name. 
[const, restricted]
map downloadUrl(hash: text, name: text) : text as "downloadurl(txt,txt)"

/// Returns an URL for the file at the provided path in /files, and (if known at compile-time) remembers that said file is referenced by this script.
[const, restricted]
map fileUrl(path: text) : text as "fileurl(txt)"

/// Returns an URL for the folder at the provided path in /files, and (if known at compile-time) remembers that said folder is referenced by this script.
[const, restricted]
map folderUrl(path: text) : text as "folderurl(txt)"

/// Returns a preview URL for the file, identified by the hash, at the provided path in /files, and (if known at compile-time) remembers that said file is referenced by this script.
[const, restricted]
map previewUrl(hash: text, path: text) : text as "previewurl(txt,txt)"

/// Returns an URL leading to the provided runflow sequence.
[restricted]
map sequenceUrl(project: number) : text as "sequenceurl(num)"

/// Returns an URL leading to the provided sync project.
[restricted]
map syncUrl(project: number) : text as "syncurl(num)"

/// Returns an URL leading to the provided discuss task.
[restricted]
map taskUrl(task: number) : text as "taskurl(num)"

/// Returns an URL leading to the provided discuss task, focusing on the given activity.
[restricted]
map taskUrl(task: number, activity: number) : text as "taskurl(num,num)"

# We allow generic string in "pseudo" operator name here,
# to avoid problem while tokenizing ':'

/// Generates a deterministic, nearly unique number for each text.
[equality] map hash(a: text) : number as "hash(txt)"
/// Generates a deterministic, nearly unique number for each text.
[equality] map hash(a: ordinal) : long as "hash(ord)"

/// Compose text from one or more values of any type.
[const] map concat(a: boolean, *) : text as "concat"
/// Compose text from one or more values of any type.
[const] map concat(a: date, *) : text as "concat"
/// Compose text from one or more values of any type.
[const] map concat(a: markdown, *) : text as "concat"
/// Compose text from one or more values of any type.
[const] map concat(a: number, *) : text as "concat"
/// Compose text from one or more values of any type.
[const] map concat(a: text, *) : text as "concat"

/// Compose text from one or more values of any type.
[const] map concat(a: text, b: text) : text as "concat(txt,txt)"
/// Compose text from one or more values of any type.
[const] map concat(a: text, b: number) : text as "concat(txt,num)"
/// Compose text from one or more values of any type.
[const] map concat(a: text, b: date) : text as "concat(txt,date)"
/// Compose text from one or more values of any type.
[const] map concat(a: text, b: boolean) : text as "concat(txt,bool)"
/// Compose text from one or more values of any type.
[const] map concat(a: text, b: long) : text as "concat(txt,long)"
/// Compose text from one or more values of any type.
[const] map concat(a: text, b: double) : text as "concat(txt,dbl)"
/// Compose text from one or more values of any type.
[const] map concat(a: text, b: week) : text as "concat(txt,week)"
/// Compose text from one or more values of any type.
[const] map concat(a: text, b: month) : text as "concat(txt,mnth)"


/// Canonical text representation of a number, same as "\{a}".
[const] map text(a: number) : text as "str(num)"

/// Canonical text representation of a date, same as "\{a}".
[const] map text(a: date) : text as "str(date)"

/// Canonical text representation of a Boolean, same as "\{a}".
[const] map text(a: boolean) : text as "str(bool)"

/// A text representation of the set
map text(set: flagset): text as "printSet(flag)"

/// Identity function, same as "\{a}".
[const]
map text(a: text) : text as "identity(txt)"

[const] map text(a: long) : text as "str(long)"
[const] map text(a: week) : text as "str(week)"
[const] map text(a: month) : text as "str(mnth)"
[const] map text(a: double) : text as "str(dbl)"
[const, private] map text(a: markdown) : markdown as "identity(markdown)"

/// Does a piece of text start with another ?
[const] map startsWith(
    /// String to be verified
    t: text,
    /// Searched prefix
    prefix: text) : boolean as "startsWith"

/// Does a piece endsWith text end with another ?
[const] map endsWith(
    /// String to be verified
    t: text,
    /// Searched suffix
    suffix: text) : boolean as "endsWith"

/// Remove all characters at the beginning and end of text t
/// that are in toTrim
/// Usage:
///   trim(" # abcde###  ",   "") == " # abcde###  "
///   trim(" # abcde###  ", " #") == "abcde"
///   trim(" # abcde###  ",  " ") == "# abcde###"
[const] map trim(
    /// Text to be trimmed
    t: text,
    /// String with all the characters we want to trim.
    toTrim: text) : text as "trim(txt,txt)"

/// Remove all characters from the text t that are in toTrim
/// Usage:
///   trimAll(" # abcde###  ",   " c#") == "abde"
[const] map trimAll(
    /// Text to be trimmed
    t: text,
    /// String with all the characters we want to trim.
    toTrim: text) : text as "trimAll(txt,txt)"

/// Length in characters of the text.
/// Edge cases may appear with complex unicode scripts.
[const] map strlen(a: text) : number as "strlen"

/// Detect if pattern match the haystack, with a non-changing
/// pattern
[const] map like(haystack: text; pattern: text) : boolean as "like(txt;txt)"

/// Detect if pattern match the haystack
[const] map like(value: text, pattern: text) : boolean as "like(txt,txt)"

/// Indicate whether a text value contains another.
[const] map contains(
    /// Full text
    haystack: text,
    /// Text to be searched in the haystack
    needle: text) : boolean as "contains"

/// Count the number of fields within a given text value according to a separator.
[const] map fieldCount(
    /// Full text
    haystack: text,
    /// Text representing a separator
    sep: text) : number as "fieldCount(txt,txt)"

/// Indicate whether a haystack contains any needle as separated by a separator.
map containsAny(
    /// Full text
    haystack: text,
    /// Needles to be searched in the haystack
    needles: text,
    /// Text field separator
    sep: text) : boolean as "containsAny"

/// Count non-overlapping occurrences of a needle inside the haystack.
[const] map containsCount(
    /// Full text
    haystack: text,
    /// Text to be searched in the haystack
    needle: text) : number as "containsCount"

/// Indicate whether a text appears in another text.
/// Returns a negative number if not found.
[const] map indexOf(
    /// Text to be searched in
    haystack: text,
    /// Text to search.
    needle: text) : number as "indexOf(text,text)"

/// Replace a sequence with another within text.
[const]
map replace(
    /// Text to transform
    t: text,
    /// Text to be replaced
    needle: text,
    /// Text to be inserted in place of needle.
    replacement: text) : text as "replace"

/// Quote a text value, escaping any special characters inside.
map escape(
    /// Text to escape
    t: text) : text as "escape"

/// Quote a text value, escaping characters used for the like operator.
map escapelike(
    /// Text to escape
    t: text) : text as "escapelike"

/// Pad a string up to a specific length by prepending
/// characters to the left.
[const] map padLeft(
    /// Text to pad
    t: text,
    /// The padding character or text. If more than one character,
    /// the entire padding will be prepended unsliced, which may
    /// bring the total length above the length target.
    pad: text,
    /// Prepend the padding text until the length of the
    /// resulting text is greater than or equal to this value.
    length: number) : text as "padLeft"

/// Sanitize a text value. Invisible characters are skipped.
/// Returns the sanitized text or raises an error if invalid
/// characters are present in the data.
map sanitize(
    /// Text to be sanitized
    t: text) : text as "sanitize"

/// Drop the first count characters of the string
/// Example:
///   substr("abcdefgh", 3) == "defgh"
///   substr("abcdefgh", -1) == "h"
[const]
map substr(
    /// Text to cut
    text: text,
    /// Number of characters to drop if positive,
    /// negative value are offset expressed from
    /// the end of the string.
    dropOffset: number) : text as "substr(text,num)"

/// Extract a subtext at a given position and length.
[const]
map substr(
    /// Text to chop
    text: text,
    /// 0-based index to start extraction
    offset: number,
    /// Number of characters to take
    length: number) : text as "substr(text,num,num)"

/// The upper-case version of a text.
[const] map uppercase(a: text) : text as "uppercase"

/// The lower-case version of a text.
[const] map lowercase(a: text) : text as "lowercase"

/// A field in a delimited row
/// Usage:
///   field("a-b-c", "-", 0) == "a"
///   field("a-b-c", "-", 1) == "b"
///   field("a-b-c", "-", 3) == ""
[const]
map field(
    /// Row text
    row: text,
    /// Separator to break the row in cells ("|" for example)
    separator: text,
    /// 0 based index of the field.
    index: number) : text as "field(txt,txt,num)"

/// A field from the right in a delimited row
/// Usage:
///   fieldr("a-b-c", "-", 0) == "c"
///   fieldr("a-b-c", "-", 1) == "b"
///   fieldr("a-b-c", "-", 3) == ""
[const]
map fieldr(
    /// Row text
    row: text,
    /// Separator to break the row in cells ("|" for example)
    separator: text,
    /// 0 based index of the field.
    index: number) : text as "field.r(txt,txt,num)"

[sideeffect] map throw_with_message__(a: text) : text as "throw"

[const] map format(v: date, format: text) : text as "format(date)"
[const] map format(v: month, format: text) : text as "format(mnth)"
[const] map format(v: week, format: text) : text as "format(week)"
[const] map format(v: number, format: text) : text as "format(num)"
[const] map format(v: double, format: text) : text as "format(dbl)"
[const] map format(v: long, format: text) : text as "format(long)"


/// A string representing the hour in the chosen format.
/// The format should follow the .Net custom format specifiers
/// Usage: 
///		printTime(0.45,"hh/mm/ss")
///		printTime(0.5," "Il est "hh\h")
[sideeffect] map printTime(
    /// The fraction of the day
    fraction: number, 
    /// The format to display the hour
    format: text) : text as "printTime(num,txt)"

[sideeffect] map printTime(
    /// The fraction of the day
    fraction: number) : text as "printTime(num)"

/// Boolean negation
[const]
map not(a: boolean) : boolean as "not"

/// Constructs a hex color string from three [0..1] components
[const] map rgb(r: number, g: number, b: number) : text as "rgb"

/// Create an error message and make the script run fail
///
/// This method is intended to be used in the context of
/// user functions only.
[sideeffect, const] map assertfail(
    /// User readable message that will be displayed as
    /// an error in the run results
    msg : text) : boolean as "assertfail(txt)"

##########################################################################
##  Un-callable internal functions
##########################################################################

[private] map ord_to_num__(o: ordinal) : number as "to-num(ord)"
[private] map ord_gt(a: ordinal, b: ordinal) : boolean as "gt(ord,ord)"

[private] map month__(d: date) : date as "month_nvn4"
[private] map week__(d: date) : date as "week_nvn4"
[private] map weekday__(d: date) : date as "weekday"

[private, const] map min_date__(a: date, b: date) : date as "min(date,date)"
[private, const] map min_num__(a: number, b: number) : number as "min(num,num)"
[private, const] map min_text__(a: text, b: text) : text as "min(txt,txt)"

[private, const] map max_date__(a: date, b: date) : date as "max(date,date)"
[private, const] map max_num__(a: number, b: number) : number as "max(num,num)"
[private, const] map max_text__(a: text, b: text) : text as "max(txt,txt)"

[private, const] map concat_text__(a: text, b: text) : text as "concat(txt,txt)"
[private, const] map concat_num__(a: text, b: number) : text as "concat(txt,num)"
[private, const] map concat_bool__(a: text, b: boolean) : text as "concat(txt,bool)"
[private, const] map concat_date__(a: text, b: date) : text as "concat(txt,date)"

/// Log that doesn't bother with NaN
[private] map log_nan__(a : number) : number as "lognan(num)"

/// Log that doesn't bother with NaN
[private] map log_nan__(a : double) : double as "lognan(dbl)"

/// Log that doesn't bother with NaN
[private] map sqrt_nan__(a : number) : number as "sqrtnan(num)"

/// Log that doesn't bother with NaN
[private] map sqrt_nan__(a : double) : double as "sqrtnan(dbl)"

/// pownan le barbare
[private] map pow_nan__(a : number, b : number) : number as "pownan(num,num)"

/// pownan le barbare
[private] map pow_nan__(a : double, b : double) : double as "pownan(dbl,dbl)"

/// square
[private] map square__(a : number) : number as "square(num)"

/// True if the line is a hole (equivalent to 'a < 0')
[private] map ishole__(a : int) : boolean as "ishole"

/// Division that doesn't bother with NaN
[private] map div_nan__(a : number, b : number) : number as "divnan(num,num)"

/// Division that doesn't bother with double NaN
[private] map div_nan__(a : double, b : double) : double as "divnan(dbl,dbl)"

[private] map assert_epochs__(a : number) : boolean as "assertepochs(num)"

[private, const] map assert_match__(a : boolean, b : number) : boolean as "assertmatch(bool,num)"
[private, const] map assert_match__(a : boolean, b : text) : boolean as "assertmatch(bool,txt)"
[private, const] map assert_match__(a : boolean, b : boolean) : boolean as "assertmatch(bool,bool)"
[private, const] map assert_match__(a : boolean, b : date) : boolean as "assertmatch(bool,date)"
[private, const] map assert_match__(a : boolean, b : week) : boolean as "assertmatch(bool,week)"
[private, const] map assert_match__(a : boolean, b : month) : boolean as "assertmatch(bool,mnth)"


/// Fails with a 'same' error message
[private] map assert_same__(a1: text, b1: text): boolean as "assertsame(1)"

/// Fails with a tuple 'same' error message
[private] map assert_same__(a1: text, a2: text, b1: text, b2: text): boolean as "assertsame(2)"
[private] map assert_same__(a1: text, a2: text, a3: text, b1: text, b2: text, b3: text): boolean as "assertsame(3)"

/// Fails with a large tuple 'same' error message, indicating the position (one-based) and values.
[private] map assert_same_plus__(pos: number, a: text, b: text): boolean as "assertsame(+)"

/// Fails with a 'single' error message if argument is true, otherwise returns true
[private] map assert_single__(b: boolean): boolean as "assertsingle"

/// Fails with a message that depends on its argument. 
[private] map assert_notNull__(kind: number): boolean as "assertnotnull(num)"

/// Default ord value to let us implement some stdlib stuff
[private] map bad_ord__() : ordinal as "default-ord()"

/// To let us implement same(ord)
[private] map text(a : ordinal) : text as "str(ord)"

/// Get a value in a random series, returns a random 64-bit
[private] map random_series_value__(series: long, pos: long): long as "random-series-value"

/// Fork a random series into another random series, returns a new random series id
[private] map random_series_child__(series: long, pos: long): long as "random-series-child"

/// Default embedding value to let us implement some stdlib stuff
[private] map bad_emb__() : embedding as "default-emb()"
module random {
    /// Draw a sample from a binomial distribution with 1 trial.
    /// Return `true` if a success is hit. The argument `p` is the 
    /// probability of success and must be in the interval `[0, 1]`.
    map binomial(p: number, s: seed) : boolean as "random.binomial"

    /// Draw a sample from a negative binomial distribution of mean `mu` and
    /// of variance `σ^2 = d * μ` where `d` is the dispersion. The dispersion must
    /// be greater or equal to 1.
    map negativeBinomial(mu: number, dispersion: number, s: seed) : number as "random.negativeBinomial"
        
    /// Draw a sample from a zero inflated negative binomial distribution of 
    /// mean `mu` and of variance `σ^2 = d * μ` where `d` is the dispersion. 
    /// The dispersion must be greater or equal to 1. The zero inflation
    /// must be between 0 and 1.
    map negativeBinomial(mu: number, dispersion: number, zeroInflation: number, s: seed) : number as "random.negativeBinomial(num,num,num)"

    /// Draw a sample from a normal distribution of mean `0` and standard
    /// deviation `1`.
    map normal(s: seed) : number as "random.normal"

    /// Draw a sample from a normal distribution of mean `mu` and standard
    /// deviation `sigma`. The standard deviation must be non-negative.
    map normal(mu: number, sigma: number, s: seed) : number as "random.normal(num,num)"

    /// Draw a sample from a Poisson distribution of mean `lambda`.
    /// The mean must be non-negative.
    map poisson(lambda: number, s: seed) : number as "random.poisson"

    /// Draw a sample from a ranvar.
    map ranvar(a: ranvar, s: seed) : number as "random.ranvar"

    /// Draw a sample from uniform distribution over the interval `[0,1[`. 
    map uniform(s: seed) : number as "random.uniform"

    /// Draw a sample from uniform distribution over the interval `[min, max[`. 
    map uniform(min: number, max: number, s: seed) : number as "random.uniform(num,num)"

    /// Draw an integer from uniform distribution over the interval `[| 1 ; max|]`. 
    map integer(max: number, s: seed) : number as "random.integer(num)"

    /// Draw an integer from uniform distribution over the interval `[| min ; max|]`. 
    map integer(min: number, max: number, s: seed) : number as "random.integer(num, num)"

    /// Randomly shuffle the ordinal vector.
    call shuffle(x: ordinal) : ordinal as "random.shuffle(ord)"    

    /// Draw a sample from a loglogistic distribution of scale `alpha` and
    /// of shape `beta`. 'alpha' and 'beta must be greater than 0.
    map logLogistic(alpha: number, beta: number, s: seed) : number as "random.logLogistic"
}

module ranvar {
    /// convert time-series into ranvars by
    /// collecting observations over moving windows. 
    call segment<Items, Time, Censored>(
        /// first date (inclusive) for each item
        Items.start: date as "Start",
        /// End date (inclusive) for each item
        Items.end: date as "End",
        /// Length in day of period for each item
        Items.horizon: number as "Horizon",
        /// Increments in day in-between observation
        Items.step: number as "Step",
        /// Date associated to each event
        Time.Date: date as "Date",
        /// Quantity for each event
        Time.Quantity: number as "Quantity",
        Items -> Time,
        Censored.CensoredDemandDate?: date as "CensoredDemandDate",
        Censored.CensoredDemandDateStart?: date as "CensoredDemandDateStart",
        Censored.CensoredDemandDateEnd?: date as "CensoredDemandDateEnd",
        ?Items -> Censored) : Items.ranvar as "ranvar.segment"
        
    call periodicr<Items, Time, Censored>(
        Items.start: date as "Start",
        Items.end: date as "End",
        Items.horizon: ranvar as "Horizon",
        Time.Date: date as "Date",
        Time.Quantity: number as "Quantity",
        Items -> Time,
        Censored.CensoredDemandDate?: date as "CensoredDemandDate",
        Censored.CensoredDemandDateStart?: date as "CensoredDemandDateStart",
        Censored.CensoredDemandDateEnd?: date as "CensoredDemandDateEnd",
        ?Items -> Censored) : Items.ranvar as "ranvar.periodicr"

    /// Returns the ranvar represented by the function `k→1/(|n|+1)` on the segment
    /// `[0;n]` (if n≥0) or `[n;0]` (if n<0) and 0 elsewhere. 
    [sideeffect] map uniform(n: number) : ranvar as "ranvar.uniform(num)"

    ///Returns the ranvar represented by the function `k→1/(n+1−m)` on the
    /// segment `[m;n]` and 0 elsewhere. We assume that `m≤n`; an error is thrown if `m>n`.
    [sideeffect] map uniform(m: number, n: number) : ranvar as "ranvar.uniform(num,num)"

    /// Returns the ranvar explicitely defined bucket by bucket.
    [noscan] aggregator buckets(
        /// Probability of the bucket.
        p: number,
        /// Inclusive lower bound of bucket.
        min: number,
        /// Inclusive upper bound of the bucket.
        max: number) : ranvar as "ranvar.buckets"
}

module solve {
    call moq<Grid, Items>(
        /// Identifiers of the SKUs or products relevant for the MOQ optimization.
        Items.Item: text as "Item",
        /// Grid quantity, used for ordering the lines of the grid.
        Grid.Quantity: number as "Quantity",
        /// Economical reward associated with purchasing the line of the grid.
        Grid.Reward: number as "Reward",
        /// Economical cost of purchasing the line of the grid.
        Grid.Cost: number as "Cost",
        /// (optional) Target contribution associated to the grid line.
        /// Apply only when either `MaxTarget` or `MinTarget` are specified.
        Grid.Target?: number as "Target",
        Items.TargetGroup?: text as "TargetGroup",

        Items.MaxCost?: number as "MaxCost",
        Items.MaxTarget?: number as "MaxTarget",
        Items.MinTarget?: number as "MinTarget",
        Items.Group*: text as "GroupId",
        Items.GroupMinQuantity*: number as "GroupMinQuantity",
        Grid.GroupQuantity*: number as "GroupQuantity",
        Items -> Grid) : Grid.boolean as "solve.moq"
}
module stockrwd {
    /// Calculate a differential stock reward estimation
    ///
    /// The following parameters are assumed:
    ///
    ///  * `unitReward` is assumed to be `1`
    ///  * `stockoutPenalty` is assumed to be `0`
    ///  * `carryingCost` is assumed to be `0`
    [sideeffect] map m(
        /// Probability distribution of the demand
        demandDistrib: ranvar,
        /// Decay factor, should be `0 <= discount <= 1`
        decay: number) : zedfunc as "stockrwd.m"

    /// Calculate a differential stock reward estimation
    /// The following parameters are assumed:
    ///
    ///  * `unitReward` is assumed to be `0`
    ///  * `stockoutPenalty` is assumed to be `1`
    ///  * `carryingCost` is assumed to be `0`
    ///  * `decay` is assumed to be `0`
    [sideeffect] map s(
        /// Probability distribution of the demand
        demandDistrib: ranvar) : zedfunc as "stockrwd.s"

    /// Calculate a differential stock reward estimation
    /// The following parameters are assumed:
    ///
    ///  * `unitReward` is assumed to be `0`
    ///  * `stockoutPenalty` is assumed to be `0`
    ///  * `carryingCost` is assumed to be `1`
    [sideeffect] map c(
        /// Probability distribution of the demand
        demandDistrib: ranvar,
        /// Decay factor, should be `0 <= discount <= 1`
        decay: number) : zedfunc as "stockrwd.c"
}
module extend {
    /// Creates a list buckets that reflect the inner structure of the ranvar.
    tablefunc ranvar(
        r: ranvar
    ) : {
        Min: number,
        Max: number
    } as "extend.ranvar"

    /// Creates a list buckets that reflect the inner structure of the ranvar.
    tablefunc ranvar(
        r: ranvar,
        /// Two buckets [0,0] and [1,gap] are introduced.
        gap: number
    ) : {
        Min: number,
        Max: number
    } as "extend.ranvar(gap)"

    /// Creates a list buckets that reflect the inner structure of the ranvar.
    tablefunc ranvar(
        r: ranvar,
        /// Two buckets [0,0] and [1,gap] are introduced.
        gap: number,
        /// Starting from 'gap+1', buckets are 'multiplier' units long.
        multiplier: number
    ) : {
        Min: number,
        Max: number
    } as "extend.ranvar(gap,multiplier)"

    /// Creates a list buckets that reflect the inner structure of the ranvar.
    tablefunc ranvar(
        r: ranvar,
        /// Two buckets [0,0] and [1,gap] are introduced.
        gap: number,
        /// Starting from 'gap+1', buckets are 'multiplier' units long.
        multiplier: number,
        /// The last bucket goes far enough to include 'reach'.
        reach: number
    ) : {
        Min: number,
        Max: number
    } as "extend.ranvar(gap,multiplier,reach)"

    tablefunc billOfMaterials<Items, Parts, Demands>(
        Items.Item: text as "Item",
        Parts.Part: text as "Part",
        Parts.Quantity: number as "Quantity",
        Items -> Parts,
        Demands.DemandId: text as "DemandId",
        Demands.DemandValue: number as "DemandValue",
        Items -> Demands
    ) : {
        Quantity: number,
        DemandId: text
    } as "extend.billOfMaterials"

    /// Finds near optimal target parameters assuming up to level policy, stationary demand
    /// and uni-directed graph w/o cyclicities.
    tablefunc echelonSolve<Network>(
        Network.Reference: text as "Reference",
        Network.LocationId: text as "LocationId",
        Network.ParentLocationId: text as "ParentLocationId",
        Network.ReorderLeadTime: number as "ReorderLeadTime",
        Network.TransportLeadTime: number as "TransportLeadTime",
        Network.TargetServiceLevel: number as "TargetServiceLevel",
        Network.Mean: number as "Mean",
        Network.StdDeviation: number as "StdDeviation",
        Network.Moq: number as "Moq"
    ) : {
        ServiceLevel: number,
        Target: number
    } as "extend.echelonSolve"

    /// Create `n` lines for each line from the table `T` when `n` is
    /// expected to be an integer.
    tablefunc range(
        /// If n < 0, n is clamped to 0
        /// If n > 10'000 (or 1'000'000 in the scalar case) then is
        /// clamped to 10'000 (or 1'000'000)
        n: number) : { N: number } as "extend.range"

    /// Create a list of tokens splitting input text values.
    /// Empty tokens are omitted. Max 100 separators.
    tablefunc split<Phrases,Separators>(
        /// Input text value to be split.
        Phrases.Source : text,
        /// Table must not be empty. Separator must not be empty text.
        Separators.Separator : text
    ) : {
        /// The non-empty token.
        Token : text,
        /// The index of the first char in the original text value.
        Position : number
    } as "extend.split(txt,txt)"

    /// Create a list of tokens splitting input text values.
    /// A unit-split occurs if a token ends with the unit
    /// and if the unit is immediately preceded by a digit.
    /// Empty tokens are omitted. Max 100 separators.
    /// Max 100 units.
    tablefunc split<Phrases,Separators,Units>(
        /// Input text value to be split.
        Phrases.Source : text,
        /// Separator must not be empty text.
        Separators.Separator : text,
        /// Table must not be empty. Unit must not be empty text.
        Units.Unit : text
    ) : {
        /// The non-empty token.
        Token : text,
        /// The index of the first char in the original text value.
        Position : number
    } as "extend.split(txt,txt,txt)"
}

tablefunc holidays(
    start: date,
    end : date,
    zone : text
) : {
    Day : date,
    HolidayName : text
} as "holidays(date,txt)"

/// Built-in table describing the files loaded
/// by the running script.
table Files[*] : {
    /// Path of the file in big file
    Path : text,
    /// Date of the last files modification
    ModifiedDate : date,
    ModifiedHour : number,
    ModifiedMinute : number,
    /// Date of the last files content modification
    ContentChangeDate : date,
    ContentChangeHour : number,
    ContentChangeMinute : number,
    /// Date of the last files any modification
    AnyChangeDate : date,
    AnyChangeHour : number,
    AnyChangeMinute : number,
    /// Age of the file, exressed in hours
    Age: number,
    Bytes : number,
    Alias : text,
    Success : boolean,
    RawLines : number,
    BadLines : number,
    BadDates : number,
    BadMonths : number,
    BadWeeks : number,
    BadNumbers : number,
    TextTooLarge : number,
    MissingValues : number,
    /// Original file source (not the potentially converted one)
    Hash : text,
    BadEncoding : number
}

module to {
    
    /// Convert a number to a double precision number
    [const] map double(a: number) : double as "to-double(num)"

    /// Convert a long integer to a double precision
    /// Remarks: some precision can be lost, if the number is big
    [const] map double(a: long) : double as "to-double(long)"

    /// Convert a number to long integer
    /// The fractional part will be lost during the conversion
    [const] map long(a: number) : long as "to-long(num)"

    /// Convert a double precision number to long integer
    /// The fractional part will be lost during the conversion
    [const] map long(a: double) : long as "to-long(dbl)"

    /// Convert a double precision number to long integer
    /// The fractional part will be lost during the conversion
    [private] map long(a: int) : long as "to-long(int)"

    /// Convert an ordinal to a long integer
    [private] map long(a: ordinal) : long as "to-long(ord)"

    /// Convert a number to long integer
    /// The fractional part will be lost during the conversion
    [const] map number(a: long) : number as "to-num(long)"

    /// Convert a double to a number
    [const] map number(a: double) : number as "to-num(dbl)"

    /// Convert a week to a number
    [const] map number(a: week) : number as "to-num(week)"

    /// Convert a month to a number
    [const] map number(a: month) : number as "to-num(mnth)"

    /// Convert a number to a date
    [private] map date(a: number): date as "to-date(num)"

    /// Convert a number to a date
    [private] map date(a: ordinal): date as "to-date(ord)"

    /// Convert a number to a week
    [private] map week(a: number): date as "to-week(num)"

    /// Convert a number to a week
    [private] map week(a: ordinal): date as "to-week(ord)"

    /// Convert a number to a month
    [private] map month(a: number): date as "to-month(num)"

    /// Convert a number to a month
    [private] map month(a: ordinal): date as "to-month(ord)"
}