41template <
typename Path,
typename Name>
44 using PathType = Path;
45 using NameType = Name;
48 static constexpr bool SingleTag = std::is_same_v<Path, TagPath<Name>>;
57 static constexpr bool valid =
false;
64 static constexpr bool valid =
true;
65 using type = ReadItem<PathOf<T>, PathTag<PathOf<T>>>;
69template <PathValue X,
typename Name>
70struct ReadItemOf<OnPath<X, Name>>
72 static constexpr bool valid =
true;
73 using type = ReadItem<PathOf<X>, Name>;
77template <
typename... Ts>
81 if constexpr (
sizeof...(Ts) < 2)
87 return []<
typename First,
typename... Rest>(std::type_identity<First>, std::type_identity<Rest>...) {
88 return (!std::is_same_v<First, Rest> && ...) && distinctTypes<Rest...>();
89 }(std::type_identity<Ts>{}...);
106template <IsComposite T>
107struct LibraryBaseOf<T>
109 template <
typename... Blocks>
110 static Composite<Blocks...> *
111 of(std::tuple<Blocks...> *);
113 using type = std::remove_pointer_t<
decltype(of(
static_cast<CompositeBlocks<T> *
>(
nullptr)))>;
116template <IsRepeated T>
117struct LibraryBaseOf<T>
119 using type = Repeated<RepeatedBlock<T>, T::MaxCount>;
122template <IsUniformLeaf T>
123 requires(!IsPeriodic<T>)
124struct LibraryBaseOf<T>
126 using type = Linear<T::ValueDimensionStep == 0 ? T::FixedValueDimension : Eigen::Dynamic, T::MaxValueDimension>;
129template <IsPeriodic T>
130struct LibraryBaseOf<T>
132 template <PeriodicInterval Interval,
int Dimension,
int MaxDimension>
133 static Periodic<Interval, Dimension, MaxDimension> *
134 of(Periodic<Interval, Dimension, MaxDimension>
const *);
136 using type = std::remove_pointer_t<decltype(of(static_cast<T const *>(
nullptr)))>;
143using LibraryBase =
typename LibraryBaseOf<T>::type;
150template <
typename Name,
typename Block>
151inline constexpr bool sameLeaf = std::derived_from<Name, Block> || std::derived_from<Block, Name>
152 || std::is_same_v<LibraryBase<Name>, LibraryBase<Block>>;
171template <
typename... Items>
174 static constexpr std::size_t
Count =
sizeof...(Items);
180 template <std::
size_t I>
181 using Item = std::tuple_element_t<I, std::tuple<Items...>>;
194template <
typename... Items>
199 (internal::ReadItemOf<std::remove_cvref_t<Items>>::valid && ...),
200 "reads() takes paths, path<Tags...> or a tag's value, and on<Name>(path) for a path read under another "
205 internal::distinctTypes<typename internal::ReadItemOf<std::remove_cvref_t<Items>>::type::NameType...>(),
206 "two items of reads() share a name, their paths' last tag; read one under another name with on<Name>(path)"
221template <
typename... Paths>
224 static constexpr std::size_t
Count =
sizeof...(Paths);
233 template <std::
size_t I>
234 using Path = std::tuple_element_t<I, std::tuple<Paths...>>;
248template <
typename Paths_,
typename Out_>
251 using PathList = Paths_;
264 static_assert(
sizeof...(Paths) > 0,
"writes() names at least one block");
265 static_assert(internal::distinctTypes<internal::PathOf<Paths>...>(),
"writes() names each block once");
276template <
typename... Names>
279 static constexpr std::size_t
Count =
sizeof...(Names);
292 "affineIn() names an input of the model by the one tag it is read under in reads(), never by a path into the "
296 internal::distinctTypes<internal::PathTag<internal::PathOf<Names>>...>(),
297 "affineIn() names each input once"
307inline constexpr bool isGaussianMixture =
false;
310template <
typename Scalar,
typename Space,
typename Allocator>
311inline constexpr bool isGaussianMixture<GaussianMixtureParams<Space, Scalar, Allocator>>{
true};
318inline constexpr bool isNoiseCovariance = MatrixExpression<T>;
322inline constexpr bool placesCovariance =
false;
325template <
typename X,
typename Path>
326inline constexpr bool placesCovariance<OnPath<X, Path>> = isNoiseCovariance<X>;
328template <
typename X,
typename Path>
329inline constexpr bool placesCovariance<OnEach<X, Path>> = isNoiseCovariance<X>;
336inline constexpr bool isPlacedNoise =
337 isPlacedFactor<std::remove_cvref_t<T>> || placesCovariance<std::remove_cvref_t<T>>;
343inline constexpr bool placesMixture =
false;
346template <
typename X,
typename Path>
347inline constexpr bool placesMixture<OnPath<X, Path>> = isGaussianMixture<std::remove_cvref_t<X>>;
349template <
typename X,
typename Path>
350inline constexpr bool placesMixture<OnEach<X, Path>> = isGaussianMixture<std::remove_cvref_t<X>>;
355template <
typename Axis,
typename Part>
377template <
typename... Parts>
380 static_assert(
sizeof...(Parts) > 0,
"additiveNoise() takes a distribution, or parts placed by tag path");
382 (internal::isPlacedNoise<Parts> && ...) || (
sizeof...(Parts) == 1 && !(internal::isPlacedNoise<Parts> || ...)),
383 "additiveNoise() with no placement covers the whole output space, so it takes one distribution alone; place "
384 "each part with on<Path>() or onEach<Path>() to give several"
387 (!internal::placesMixture<std::remove_cvref_t<Parts>> && ...),
388 "a mixture is additive noise over the whole output space, unplaced; a placed part is one Gaussian"
394 static constexpr bool Placed = (internal::isPlacedNoise<Parts> || ...);
397 using Distribution = std::remove_cvref_t<std::tuple_element_t<0, std::tuple<Parts...>>>;
400 static constexpr bool ZeroMean = !
Placed && internal::isNoiseCovariance<Distribution>;
407 : m_parts{std::forward<Parts>(
parts)...}
411 [[nodiscard]] std::tuple<Parts...>
const &
422 return std::get<0>(m_parts);
429 [[nodiscard]] std::tuple_element_t<0, std::tuple<Parts...>>
433 return std::get<0>(std::move(m_parts));
439 template <
typename Axis>
444 return ModeNoise<Axis, std::tuple_element_t<0, std::tuple<Parts...>>>{*
this};
448 template <
typename Axis>
453 return ModeNoise<Axis, std::tuple_element_t<0, std::tuple<Parts...>>>{std::move(*
this)};
459 static constexpr void
463 !
Placed && internal::isGaussianMixture<Distribution>,
464 "byMode() takes additive noise that is one mixture over the whole output, with one component per value "
469 std::tuple<Parts...> m_parts;
495template <
typename Axis,
typename Part>
521template <
typename... Parts>
526 std::forward<Parts>(parts)...
543template <
typename... Parts>
546 static_assert(
sizeof...(Parts) > 0,
"continuousNoise() takes a spectral density, or parts placed by tag path");
548 (internal::isPlacedByPath<std::remove_cvref_t<Parts>> && ...)
549 || (
sizeof...(Parts) == 1 && !(internal::isPlacedByPath<std::remove_cvref_t<Parts>> || ...)),
550 "continuousNoise() with no placement covers the whole output space, so it takes one spectral density alone; "
551 "place each part with on<Path>() or onEach<Path>() to give several"
557 static constexpr bool Placed = (internal::isPlacedByPath<std::remove_cvref_t<Parts>> || ...);
561 : m_parts{std::move(
parts)...}
565 [[nodiscard]] std::tuple<Parts...>
const &
573 std::tuple<Parts...> m_parts;
580template <
typename... Parts>
581[[nodiscard]] ContinuousNoise<std::remove_cvref_t<Parts>...>
608inline constexpr bool isModel =
requires {
requires std::remove_cvref_t<T>::IsModel; };
638 return {std::forward<G>(function)};
664[[nodiscard]]
Sample<std::remove_cvref_t<G>>
667 return {std::forward<G>(function)};
690 template <
typename StateSpace>
692 in([[maybe_unused]] StateSpace
const & stateSpace)
const
697 std::invocable<X const &, StateSpace const &>,
698 "a model's outputSpace() function takes the state's space instance"
700 using Result = std::remove_cvref_t<std::invoke_result_t<X const &, StateSpace const &>>;
701 static_assert(
Space<Result>,
"a model's outputSpace() function returns the output space instance");
702 return value(stateSpace);
716 requires(!MatrixExpression<X>)
717[[nodiscard]] OutputSpace<std::remove_cvref_t<X>>
720 return {std::forward<X>(x)};
742inline constexpr std::optional<PolicyKind> policyKind = std::nullopt;
745template <
typename... Items>
746inline constexpr std::optional<PolicyKind> policyKind<Reads<Items...>>{PolicyKind::reads};
748template <
typename... Paths>
749inline constexpr std::optional<PolicyKind> policyKind<Writes<Paths...>>{PolicyKind::writes};
751template <
typename Paths,
typename Out>
752inline constexpr std::optional<PolicyKind> policyKind<WritesAs<Paths, Out>>{PolicyKind::writes};
754template <
typename... Names>
755inline constexpr std::optional<PolicyKind> policyKind<AffineIn<Names...>>{PolicyKind::affineIn};
757template <
typename... Parts>
758inline constexpr std::optional<PolicyKind> policyKind<AdditiveNoise<Parts...>>{PolicyKind::additiveNoise};
760template <
typename Axis,
typename Part>
761inline constexpr std::optional<PolicyKind> policyKind<ModeNoise<Axis, Part>>{PolicyKind::additiveNoise};
763template <
typename... Parts>
764inline constexpr std::optional<PolicyKind> policyKind<ContinuousNoise<Parts...>>{PolicyKind::continuousNoise};
767inline constexpr std::optional<PolicyKind> policyKind<LogLikelihood<G>>{PolicyKind::logLikelihood};
770inline constexpr std::optional<PolicyKind> policyKind<Sample<G>>{PolicyKind::sample};
773inline constexpr std::optional<PolicyKind> policyKind<OutputSpace<X>>{PolicyKind::outputSpace};
776inline constexpr std::optional<PolicyKind> policyKind<DiscreteNoise<G>>{PolicyKind::discreteNoise};
781concept ModelPolicy = policyKind<std::remove_cvref_t<T>>.has_value();
785inline constexpr bool isAdditiveNoise = policyKind<std::remove_cvref_t<T>> == PolicyKind::additiveNoise;
790template <
typename Noise>
791struct NoiseModeAxisOf
798template <
typename Axis,
typename Part>
799struct NoiseModeAxisOf<ModeNoise<Axis, Part>>
809template <
typename Noise>
810using NoiseModeAxis =
typename NoiseModeAxisOf<Noise>::type;
817template <
typename... Policies>
821 (ModelPolicy<Policies> && ...),
822 "a model takes its function and then its policies, in any order: reads(), writes(), affineIn(), "
823 "additiveNoise(), continuousNoise(), logLikelihood(), and outputSpace()"
827 static constexpr std::array<PolicyKind,
sizeof...(Policies)> kinds{*policyKind<std::remove_cvref_t<Policies>>...};
830 [[nodiscard]]
static consteval std::size_t
831 position(PolicyKind kind)
833 for (std::size_t i = 0; i < kinds.size(); ++i)
835 if (kinds[i] == kind)
844 [[nodiscard]]
static consteval bool
847 for (std::size_t i = 0; i < kinds.size(); ++i)
849 for (std::size_t j = i + 1; j < kinds.size(); ++j)
851 if (kinds[i] == kinds[j])
860 static_assert(distinct(),
"a model takes each policy at most once");
863 template <PolicyKind K>
864 static constexpr bool has = position(K) <
sizeof...(Policies);
867 template <PolicyKind K>
868 using Type = std::conditional_t<
870 std::tuple_element_t<has<K> ? position(K) : 0, std::tuple<Policies..., NoPolicy>>,
874 template <PolicyKind K,
typename Tuple>
875 [[nodiscard]]
static auto const &
876 get(Tuple
const & policies)
878 static_assert(has<K>,
"the model was not given this policy");
879 return std::get<has<K> ? position(K) : 0>(policies);
886template <
typename Space,
typename Part>
887[[nodiscard]] Part
const &
888noiseFactor(Space
const &, Part
const & part)
896 Eigen::Matrix<typename X::Scalar, X::RowsAtCompileTime, 1, Eigen::ColMajor, X::MaxRowsAtCompileTime, 1>;
899template <
typename Space, MatrixExpression X,
typename Path>
901noiseFactor(Space
const & space, OnPath<X, Path>
const & part)
903 auto block = pathSubspace<Path>(space);
904 using Noise = GaussianParams<
decltype(
block),
typename X::Scalar>;
905 return OnPath<Noise, Path>{Noise{std::move(
block), ZeroMeanOf<X>::Zero(part.value.rows()), part.value}};
911template <
typename Space, MatrixExpression X,
typename Path>
913noiseFactor(Space
const &, OnEach<X, Path>
const & part)
915 using Block =
typename EachBlockOf<Space, Path>::type;
916 if constexpr (
requires {
typename Block::BlockType; })
918 using Noise = GaussianParams<typename Block::BlockType, typename X::Scalar>;
919 return OnEach<Noise, Path>{Noise{ZeroMeanOf<X>::Zero(part.value.rows()), part.value}};
925 "onEach<Path>(R) in additiveNoise() places R on each repetition of a repeated block"
936template <
typename Space,
typename... Parts>
938placeNoise(Space
const & space, AdditiveNoise<Parts...>
const & noise)
940 if constexpr (AdditiveNoise<Parts...>::ZeroMean)
942 auto const & cov = noise.distribution();
943 using Cov =
typename AdditiveNoise<Parts...>::Distribution;
947 Rows == Eigen::Dynamic || Cov::RowsAtCompileTime == Eigen::Dynamic || Cov::RowsAtCompileTime == Rows,
948 "additiveNoise(R) is over the output's differences, so R has a row for each"
952 &&
"additiveNoise(R) is over the output's differences, so R is square with a row for each"
954 using Noise = GaussianParams<Linear<Rows, maxValueDimensionAtCompileTime<Differences>>,
typename Cov::Scalar>;
955 return AdditiveNoise<Noise>{Noise{ZeroMeanOf<Cov>::Zero(cov.rows()), cov}};
957 else if constexpr (AdditiveNoise<Parts...>::Placed)
959 auto const product = std::apply(
960 [&](
auto const &... parts) {
961 return Product{space, noiseFactor(space, parts)...};
965 using Scalar =
typename decltype(product)::Scalar;
968 auto const moments = product.moments();
969 if constexpr (isExpected<std::remove_cvref_t<
decltype(moments)>>)
971 EMRYS_FILTER_ASSERT(moments &&
"the noise's parts must have moments");
972 return AdditiveNoise<Noise>{Noise{moments->mean(), moments->covarianceLower()}};
976 return AdditiveNoise<Noise>{Noise{moments.mean(), moments.covarianceLower()}};
981 return AdditiveNoise<
typename AdditiveNoise<Parts...>::Distribution
const &>{noise.distribution()};
991template <
typename R,
typename Space>
992[[nodiscard]]
consteval bool
995 return [&]<std::size_t... I>(std::index_sequence<I...>) {
996 auto const check = []<
typename Item>(std::type_identity<Item>) {
997 using Path =
typename Item::PathType;
998 using Name =
typename Item::NameType;
999 constexpr bool inState = [] {
1000 return []<
typename First,
typename... Rest>(TagPath<First, Rest...> *) {
1001 return countTag<First, Space>() > 0;
1002 }(
static_cast<Path *
>(
nullptr));
1004 if constexpr (inState)
1007 sameLeaf<Name, PathTag<Path>>,
1008 "a name given by on<Name>() in reads() must have the same leaf space beneath it as the block it "
1009 "names, such as Linear<2> for a Position : Linear<2>"
1014 return (check(std::type_identity<
typename R::template Item<I>>{}) && ...);
1015 }(std::make_index_sequence<R::Count>{});
1029template <Space S,
typename Vector>
1046template <FixedSizeSpace S, MatrixExpression V>
1051 using Plain =
typename internal::CrtpDerived<V>::PlainObject;
1052 static_assert(!internal::IsUntagged<S>,
"a sure input is named by a tag, a type derived from a space");
1053 static_assert(Plain::ColsAtCompileTime == 1,
"a sure input is a column vector");
1056 "a sure input's rows do not match its space's dimension at compile time"
1058 EMRYS_FILTER_ASSERT(value.rows() ==
valueDimension(S{}) &&
"a sure input must be a value in its space");
1066template <Space S, MatrixExpression V>
1070 using Plain =
typename internal::CrtpDerived<V>::PlainObject;
1071 static_assert(!internal::IsUntagged<S>,
"a sure input is named by a tag, a type derived from a space");
1072 static_assert(Plain::ColsAtCompileTime == 1,
"a sure input is a column vector");
1073 EMRYS_FILTER_ASSERT(value.rows() ==
valueDimension(space) &&
"a sure input must be a value in its space");
1074 return Sure<S, Plain>{std::move(space), Plain{std::forward<V>(value)}};
1081template <
typename T>
1082inline constexpr bool isSure =
false;
1085template <
typename S,
typename Vector>
1086inline constexpr bool isSure<Sure<S, Vector>>{
true};
1101template <
typename... Paths>
1104 static constexpr std::size_t
Count =
sizeof...(Paths);
1118 static_assert(
sizeof...(Paths) > 0,
"consider() names at least one block of the state");
1119 static_assert(internal::distinctTypes<internal::PathOf<Paths>...>(),
"consider() names each block once");
1127template <
typename T>
1128inline constexpr bool isConsider =
false;
1131template <
typename... Paths>
1132inline constexpr bool isConsider<Consider<Paths...>>{
true};
Spaces built from other spaces (decisions.md [sizes-and-dimensions], [several-resizable-blocks]): Com...
The coordinate types (decisions.md [no-bounded-coordinates]): Linear, plain coordinates,...
How a function that stores a matrix argument takes it (decisions.md [forwarded-matrices]): the concep...
The Gaussian: its parameters GaussianParams, the Gaussian that evaluates the density,...
The macros every header includes first.
Mathematical constants and scalar functions that work in every scalar type the library accepts,...
The mixture: the parameters MixtureParams of weighted components of one type (decisions....
Product: the product of independent densities, each on its own block of a space, built with on<Path>(...
The Space concept and what every continuous space shares (decisions.md [sizes-and-dimensions]): the t...
Subspaces, and growing and shrinking blocks (decisions.md [several-resizable-blocks]).
Tag paths and the index utilities (decisions.md [tag-addressing], [index-names]): TagPath and TagPath...
Noise added to a model's output, , as additiveNoise() declares it (decisions.md [additive-noise],...
Definition Policies.h:379
AdditiveNoise(Parts... parts)
The noise of parts.
Definition Policies.h:406
std::tuple_element_t< 0, std::tuple< Parts... > > distribution() &&
Definition Policies.h:430
std::tuple< Parts... > const & parts() const
Definition Policies.h:412
auto byMode() &&
byMode() const &, moving the mixture out of a temporary noise.
Definition Policies.h:450
static constexpr bool Placed
Definition Policies.h:394
std::remove_cvref_t< std::tuple_element_t< 0, std::tuple< Parts... > > > Distribution
Definition Policies.h:397
static constexpr bool HasOwnSpace
Definition Policies.h:403
static constexpr bool ZeroMean
Definition Policies.h:400
auto byMode() const &
Definition Policies.h:441
Distribution const & distribution() const &
Definition Policies.h:419
The concatenation of Blocks, any number of them resizable.
Definition Composite.h:170
A process model's white-noise spectral density, as continuousNoise() declares it, placed by tag path ...
Definition Policies.h:545
ContinuousNoise(Parts... parts)
The noise of parts.
Definition Policies.h:560
static constexpr bool Placed
whether the parts are placed by tag path
Definition Policies.h:557
std::tuple< Parts... > const & parts() const
Definition Policies.h:566
Additive mixture noise whose components are the values of a mode axis, as additiveNoise(mixture)....
Definition Policies.h:497
Axis ModeAxisType
the path to the mode axis whose values the components are
Definition Policies.h:500
ModeNoise(AdditiveNoise< Part > noise)
noise, with its components as the values of the mode axis.
Definition Policies.h:503
A dense matrix or matrix expression, possibly cv- or reference-qualified: what a const Eigen::MatrixB...
Definition Forwarding.h:39
A path given as a value: a TagPath, such as path<Ownship, Position>, or a tag's own value,...
Definition TagPath.h:517
A type usable as the space of a distribution.
Definition Space.h:186
#define EMRYS_FILTER_NO_UNIQUE_ADDRESS
Assertion used for programmer errors (misuse of the API).
Definition Macros.h:38
Derived::Scalar logLikelihood(M const &measurement, Space const &space, Eigen::MatrixBase< Derived > const &x)
Definition Likelihood.h:433
auto affineIn(Names const &...)
Definition Policies.h:288
AdditiveNoise< typename internal::HeldOf< std::unwrap_ref_decay_t< Parts > >::type... > additiveNoise(Parts &&... parts)
Definition Policies.h:523
constexpr Consider< internal::PathOf< Paths >... > consider(Paths const &...)
Definition Policies.h:1116
OutputSpace< std::remove_cvref_t< X > > outputSpace(X &&x)
Definition Policies.h:718
auto writes(Paths const &...)
Definition Policies.h:262
auto sure(V &&value)
Definition Policies.h:1049
ContinuousNoise< std::remove_cvref_t< Parts >... > continuousNoise(Parts &&... parts)
Definition Policies.h:582
auto reads(Items const &...)
Definition Policies.h:196
Sample< std::remove_cvref_t< G > > sample(G &&function)
Definition Policies.h:665
Eigen::Index valueDimension(S const &space)
Definition Space.h:272
auto block(Space auto const &space, X &&x)
Definition TagPath.h:979
Eigen::Index differenceDimension(S const &space)
Definition Space.h:284
typename internal::DifferenceSpaceOf< S >::type DifferenceSpace
The space that differences of values in S live in: the type of its difference(), or S itself if it ha...
Definition Space.h:227
constexpr int valueDimensionAtCompileTime
The value dimension of Space at compile time, or Eigen::Dynamic if it is resizable.
Definition Space.h:251
constexpr int maxValueDimensionAtCompileTime
The largest valid value dimension of Space, or Eigen::Dynamic if it is unbounded.
Definition Space.h:258
The input names a model's function is affine in, given the rest, as affineIn() declares them (decisio...
Definition Policies.h:278
static constexpr std::size_t Count
the number of names
Definition Policies.h:279
The blocks of the state that a Kalman update considers rather than estimates, as consider() names the...
Definition Policies.h:1103
static constexpr std::size_t Count
the number of blocks considered
Definition Policies.h:1104
A measurement model's own at a state point, as logLikelihood() declares it, for the filters that wei...
Definition Policies.h:624
G function
as g(in, x, z)
Definition Policies.h:625
A model's output space given by its instance, or computed from the state's space, as outputSpace() de...
Definition Policies.h:681
X value
the instance, or the function
Definition Policies.h:685
static constexpr bool Computed
whether the output space is a function of the state's space
Definition Policies.h:683
auto in(StateSpace const &stateSpace) const
Definition Policies.h:692
What a model reads, as reads() declares it: the items, each a path into the state or the tag of an in...
Definition Policies.h:173
Composite< typename Items::NameType... > InputSpace
The input space: the Composite of the names, in order.
Definition Policies.h:177
std::tuple_element_t< I, std::tuple< Items... > > Item
The item at position I.
Definition Policies.h:181
static constexpr std::size_t Count
Definition Policies.h:174
A draw of the observation that a measurement model known by its likelihood alone makes of a state,...
Definition Policies.h:653
G function
a draw of as g(in, x, rng)
Definition Policies.h:654
A sure input of predict() or observe(): a known value in the tagged space S, as sure() marks it (deci...
Definition Policies.h:1031
Vector value
the value, a point of the space
Definition Policies.h:1038
S SpaceType
the input's space, a tag
Definition Policies.h:1033
S space
the space, with its sizes
Definition Policies.h:1037
Vector VectorType
the value's type
Definition Policies.h:1035
A path of tags, each naming a block inside the one before: TagPath<Ownship, Position> is the Position...
Definition TagPath.h:353
A list of tag paths, each a tag or a TagPath, such as the blocks a measurement reads (TagPaths<Positi...
Definition TagPath.h:363
What a model writes, as writes() declares it: the blocks its function's result fills (decisions....
Definition Policies.h:223
static constexpr std::size_t Count
Definition Policies.h:224
Composite< internal::PathTag< Paths >... > OutputSpace
The output space: the Composite of the blocks, in order.
Definition Policies.h:227
std::tuple_element_t< I, std::tuple< Paths... > > Path
The path at position I.
Definition Policies.h:234
TagPaths< Paths... > PathList
The paths, as a TagPaths.
Definition Policies.h:230