Feel.Analysis.Refinement.Operations (feel v0.1.0)

Copy Markdown View Source

What the operators do to sets of values: the part of typing an operation that is refinement (D-133).

Feel.Analysis.Inference types left + right, -operand, left < right. It works out the kind of the result from the kinds of the operands (a number, a date, a duration, a boolean) and whether the result can be null. What a type can say besides is which values the result can have, [0..150] + 1 being [1..151], and whether the values of the operands decide a comparison, [0..150] < 200 being true. That is the arithmetic on sets and the proofs about them, and it is this module's: it is the one place that Inference reaches Feel.Analysis.Intervals, Feel.Analysis.DateArithmetic, Feel.Analysis.MomentArithmetic, Feel.Analysis.MomentTests and Feel.Analysis.MonthScaling through, and so is part of the seam that Feel.Analysis.Refinement is (D-133).

The functions take and give sets of numbers (Feel.Type.NumberSet): the numbers of a number, or the numbers that stand for a date, a duration, a time and a date and time (Feel.Type.Image). They know nothing of kinds besides what an operation itself needs (a date shifted by a duration of months and one shifted by a duration of seconds are not the same arithmetic), and nothing of null, which is the second part of what most of them answer: whether the operation can give null because of the values it is given, an overflow, a zero divisor, a year out of the calendar. It is the caller that puts the sets into the alternatives of a type, and null into the union.

FunctionOfGives
arithmetic/3two sets of numbers, or of the images of durationsthe set of the result, and whether it can be null
arithmetic_of_one/3one set of numbers, read twice as one valuethe set of the result, and whether it can be null
negate/1a set of numbers, or of the images of a durationthe set of the result
divide_months/2the months of two durationsthe numbers of the quotient, and whether it can be null
divide_months_of_one/1the months of one duration, read twice as one valuethe numbers of the quotient, and whether it can be null
scale_months/3the months of a duration, and a numberthe months of the product or the quotient
seconds_between/2the days of two datesthe seconds between them
shift_date/4the days of a date, and a durationthe days of the date moved, and whether it can be null
shift_moment/5, moment_difference/4a time or a date and time, and a duration or anotherthe seconds, and whether it can be null
decide/3, decide_moments/3two setswhether the comparison is true for every pair, for none, or for some
null_bounds/3two sets of numberswhich bounds on them would rule null out (D-161)

They are functions of sets and need no tier: in :core the operands have no sets, and Inference does not ask for the arithmetic on the whole range of two kinds more than once (D-133).

Summary

Types

The kinds of duration that a date, a time or a date and time can be moved by.

The numbers of a result, and whether it can be null because of the operation.

Functions

The numbers that left <operator> right can be, for the sets of numbers of the operands, and whether the result can be null: an overflow past decimal128, a result too small to write, a division by zero, a power that has no value (Feel.Analysis.Intervals, D-39).

The numbers that x <operator> x can be for one value x of the set that is read twice (D-170), and whether the result can be null: x - x is 0, x / x is 1 unless x is 0, x * x is not below 0, and x + x is 2x.

Whether left <operator> right is true for every pair of numbers of the two sets (:always), for none (:never), or for some (:unknown); the sets of numbers, and of the images of dates and durations, which are ordered as numbers are (Feel.Analysis.Intervals).

The same for the sets of a time or a date and time, which are ordered by the clock or by the instant, as a pair of them can differ in (Feel.Analysis.MomentTests, D-109).

The numbers that the quotient of two years and months durations can be, for the months of the dividend and of the divisor, and whether it can be null. The run makes a number of the months of each, which it cannot for 10^34 months and more, so those are cut and the result can be null for them (Feel.Analysis.MonthScaling).

The numbers that a years and months duration divided by itself can be, for its months, and whether it can be null (D-170, D-207): the two operands are one value, so the quotient is 1 for a month that is not 0, as it is for a number, and null for 0 and for the months that the run cannot make a number of, 10^34 and more (Feel.Analysis.MonthScaling). A set with no month the run can use is no number, and null.

The seconds between two times, two dates and times, or a date and a date and time, to a second either way, and whether the result can be null (one has a zone and the other has none, D-109). The kinds are those of the operands. It is :error for a pair of kinds that has no difference (Feel.Analysis.MomentArithmetic).

The numbers that -operand can be. Negation cannot overflow, so there is no null to say.

Which bounds on the operands of left <operator> right would rule null out (D-161), for the sets of numbers of two operands that cannot be null: :near_zero for operands at least 0.001 away from zero, :too_large for operands of at most 1E+30, :range where the result needs both, and nil where it is not null, where it is certain (two single numbers), or where these bounds do not rule it out. The operands are restricted as the hint says, and the result has no null left. Feel.Analysis.Inference.null_bounds/3 is the one that takes types. With same: true the two sets are one value read twice (D-170): x / x is null only for a zero, so the bounds that rule it out are those that keep x away from zero, and a large x is no reason.

The months of a years and months duration times a number, or divided by one, for the months of a set and the numbers of another, as the run rounds them (Feel.Analysis.MonthScaling, D-109), and whether the result can be null. It is :error for a set that has no whole month.

The seconds between two dates, for the days of each: each day is 86,400 seconds.

The days that a date of the set of days plus (:add) or minus (:subtract) a duration can be, and whether the result can be null (the year can leave the calendar). It is :error if either set has no value (Feel.Analysis.DateArithmetic, D-109).

The seconds that a time or a date and time plus (:add) or minus (:subtract) a duration can be, and whether the result can be null (Feel.Analysis.MomentArithmetic, D-109). It is :error if the duration has no value that the move takes.

Types

duration_kind()

@type duration_kind() :: :years_and_months_duration | :days_and_time_duration

The kinds of duration that a date, a time or a date and time can be moved by.

result()

@type result() :: Feel.Analysis.Intervals.result()

The numbers of a result, and whether it can be null because of the operation.

Functions

arithmetic(operator, a, b, options \\ [])

The numbers that left <operator> right can be, for the sets of numbers of the operands, and whether the result can be null: an overflow past decimal128, a result too small to write, a division by zero, a power that has no value (Feel.Analysis.Intervals, D-39).

iex> alias Feel.Type.NumberSet
iex> to_150 = NumberSet.interval(Decimal.new(0), Decimal.new(150))
iex> {set, null?} = Feel.Analysis.Refinement.Operations.arithmetic(:add, to_150, NumberSet.point(Decimal.new(1)))
iex> {NumberSet.format(set), null?}
{"[1..151]", false}

arithmetic_of_one(operator, a, options \\ [])

@spec arithmetic_of_one(
  :add | :subtract | :multiply | :divide,
  Feel.Type.NumberSet.t(),
  Feel.Analysis.Intervals.options()
) :: result()

The numbers that x <operator> x can be for one value x of the set that is read twice (D-170), and whether the result can be null: x - x is 0, x / x is 1 unless x is 0, x * x is not below 0, and x + x is 2x.

iex> alias Feel.Type.NumberSet
iex> to_150 = NumberSet.interval(Decimal.new(0), Decimal.new(150))
iex> {set, null?} = Feel.Analysis.Refinement.Operations.arithmetic_of_one(:subtract, to_150)
iex> {NumberSet.format(set), null?}
{"0", false}

decide(operator, left, right)

Whether left <operator> right is true for every pair of numbers of the two sets (:always), for none (:never), or for some (:unknown); the sets of numbers, and of the images of dates and durations, which are ordered as numbers are (Feel.Analysis.Intervals).

iex> alias Feel.Type.NumberSet
iex> to_150 = NumberSet.interval(Decimal.new(0), Decimal.new(150))
iex> Feel.Analysis.Refinement.Operations.decide(:lt, to_150, NumberSet.point(Decimal.new(200)))
:always

decide_moments(operator, left, right)

The same for the sets of a time or a date and time, which are ordered by the clock or by the instant, as a pair of them can differ in (Feel.Analysis.MomentTests, D-109).

divide_months(dividend, divisor)

@spec divide_months(Feel.Type.NumberSet.t(), Feel.Type.NumberSet.t()) :: result()

The numbers that the quotient of two years and months durations can be, for the months of the dividend and of the divisor, and whether it can be null. The run makes a number of the months of each, which it cannot for 10^34 months and more, so those are cut and the result can be null for them (Feel.Analysis.MonthScaling).

divide_months_of_one(months)

@spec divide_months_of_one(Feel.Type.NumberSet.t()) :: result()

The numbers that a years and months duration divided by itself can be, for its months, and whether it can be null (D-170, D-207): the two operands are one value, so the quotient is 1 for a month that is not 0, as it is for a number, and null for 0 and for the months that the run cannot make a number of, 10^34 and more (Feel.Analysis.MonthScaling). A set with no month the run can use is no number, and null.

iex> alias Feel.Type.NumberSet
iex> months = NumberSet.interval(Decimal.new(0), Decimal.new(14))
iex> {set, null?} = Feel.Analysis.Refinement.Operations.divide_months_of_one(months)
iex> {NumberSet.format(set), null?}
{"1", true}
iex> {set, null?} = Feel.Analysis.Refinement.Operations.divide_months_of_one(NumberSet.interval(Decimal.new(1), Decimal.new(14)))
iex> {NumberSet.format(set), null?}
{"1", false}
iex> {set, null?} = Feel.Analysis.Refinement.Operations.divide_months_of_one(NumberSet.point(Decimal.new("1E+34")))
iex> {set, null?}
{[], true}

moment_difference(left_kind, left, right_kind, right)

@spec moment_difference(
  atom(),
  Feel.Type.NumberSet.t(),
  atom(),
  Feel.Type.NumberSet.t()
) ::
  result() | :error

The seconds between two times, two dates and times, or a date and a date and time, to a second either way, and whether the result can be null (one has a zone and the other has none, D-109). The kinds are those of the operands. It is :error for a pair of kinds that has no difference (Feel.Analysis.MomentArithmetic).

negate(set)

The numbers that -operand can be. Negation cannot overflow, so there is no null to say.

iex> alias Feel.Type.NumberSet
iex> set = Feel.Analysis.Refinement.Operations.negate(NumberSet.interval(Decimal.new(1), Decimal.new(10)))
iex> NumberSet.format(set)
"[-10..-1]"

null_bounds(operator, a, b, options \\ [])

@spec null_bounds(
  Feel.IR.arithmetic_operator(),
  Feel.Type.NumberSet.t(),
  Feel.Type.NumberSet.t(),
  Feel.Analysis.Intervals.options()
) :: :near_zero | :too_large | :range | nil

Which bounds on the operands of left <operator> right would rule null out (D-161), for the sets of numbers of two operands that cannot be null: :near_zero for operands at least 0.001 away from zero, :too_large for operands of at most 1E+30, :range where the result needs both, and nil where it is not null, where it is certain (two single numbers), or where these bounds do not rule it out. The operands are restricted as the hint says, and the result has no null left. Feel.Analysis.Inference.null_bounds/3 is the one that takes types. With same: true the two sets are one value read twice (D-170): x / x is null only for a zero, so the bounds that rule it out are those that keep x away from zero, and a large x is no reason.

iex> alias Feel.Type.NumberSet
iex> to_150 = NumberSet.interval(Decimal.new(0), Decimal.new(150))
iex> Feel.Analysis.Refinement.Operations.null_bounds(:divide, to_150, NumberSet.point(Decimal.new(2)))
:near_zero
iex> Feel.Analysis.Refinement.Operations.null_bounds(:add, to_150, NumberSet.point(Decimal.new(2)))
nil

scale_months(operator, months, numbers)

@spec scale_months(
  :multiply | :divide,
  Feel.Type.NumberSet.t(),
  Feel.Type.NumberSet.t()
) ::
  {:ok, result()} | :error

The months of a years and months duration times a number, or divided by one, for the months of a set and the numbers of another, as the run rounds them (Feel.Analysis.MonthScaling, D-109), and whether the result can be null. It is :error for a set that has no whole month.

seconds_between(later, earlier)

The seconds between two dates, for the days of each: each day is 86,400 seconds.

iex> alias Feel.Type.NumberSet
iex> later = NumberSet.point(Decimal.new(10))
iex> earlier = NumberSet.point(Decimal.new(8))
iex> NumberSet.format(Feel.Analysis.Refinement.Operations.seconds_between(later, earlier))
"172800"

shift_date(days, kind, amounts, operator)

@spec shift_date(
  Feel.Type.NumberSet.t(),
  duration_kind(),
  Feel.Type.NumberSet.t(),
  :add | :subtract
) ::
  result() | :error

The days that a date of the set of days plus (:add) or minus (:subtract) a duration can be, and whether the result can be null (the year can leave the calendar). It is :error if either set has no value (Feel.Analysis.DateArithmetic, D-109).

shift_moment(kind, set, duration_kind, amounts, operator)

@spec shift_moment(
  :time | :date_and_time,
  Feel.Type.NumberSet.t(),
  duration_kind(),
  Feel.Type.NumberSet.t(),
  :add | :subtract
) :: result() | :error

The seconds that a time or a date and time plus (:add) or minus (:subtract) a duration can be, and whether the result can be null (Feel.Analysis.MomentArithmetic, D-109). It is :error if the duration has no value that the move takes.