#include "lzokay.hpp"
#include <cstring>
#include <algorithm>

/*
 * Based on documentation from the Linux sources: Documentation/lzo.txt
 * https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/lzo.txt
 */

namespace lzokay {

#if _WIN32
#define HOST_BIG_ENDIAN 0
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
#define HOST_BIG_ENDIAN 1
#else
#define HOST_BIG_ENDIAN 0
#endif

#if HOST_BIG_ENDIAN
static uint16_t get_le16(const uint8_t* p) {
  uint16_t val = *reinterpret_cast<const uint16_t*>(p);
#if __GNUC__
  return __builtin_bswap16(val);
#elif _WIN32
  return _byteswap_ushort(val);
#else
  return (val = (val << 8) | ((val >> 8) & 0xFF));
#endif
}
#else
static uint16_t get_le16(const uint8_t* p) {
  return *reinterpret_cast<const uint16_t*>(p);
}
#endif

constexpr std::size_t Max255Count = std::size_t(~0) / 255 - 2;

#define NEEDS_IN(count) \
  if (inp + (count) > inp_end) { \
    dst_size = outp - dst; \
    return EResult::InputOverrun; \
  }

#define NEEDS_OUT(count) \
  if (outp + (count) > outp_end) { \
    dst_size = outp - dst; \
    return EResult::OutputOverrun; \
  }

#define CONSUME_ZERO_BYTE_LENGTH \
  std::size_t offset; \
  { \
    const uint8_t *old_inp = inp; \
    while (*inp == 0) ++inp; \
    offset = inp - old_inp; \
    if (offset > Max255Count) { \
      dst_size = outp - dst; \
      return EResult::Error; \
    } \
  }

#define WRITE_ZERO_BYTE_LENGTH(length) \
  { \
    std::size_t l; \
    for (l = length; l > 255; l -= 255) { *outp++ = 0; } \
    *outp++ = l; \
  }

constexpr uint32_t M1MaxOffset = 0x0400;
constexpr uint32_t M2MaxOffset = 0x0800;
constexpr uint32_t M3MaxOffset = 0x4000;
constexpr uint32_t M4MaxOffset = 0xbfff;

constexpr uint32_t M1MinLen = 2;
constexpr uint32_t M1MaxLen = 2;
constexpr uint32_t M2MinLen = 3;
constexpr uint32_t M2MaxLen = 8;
constexpr uint32_t M3MinLen = 3;
constexpr uint32_t M3MaxLen = 33;
constexpr uint32_t M4MinLen = 3;
constexpr uint32_t M4MaxLen = 9;

constexpr uint32_t M1Marker = 0x0;
constexpr uint32_t M2Marker = 0x40;
constexpr uint32_t M3Marker = 0x20;
constexpr uint32_t M4Marker = 0x10;

constexpr uint32_t MaxMatchByLengthLen = 34; /* Max M3 len + 1 */

EResult decompress(const uint8_t* src, std::size_t src_size,
                   uint8_t* dst, std::size_t init_dst_size,
                   std::size_t& dst_size) {
  dst_size = init_dst_size;

  if (src_size < 3) {
    dst_size = 0;
    return EResult::InputOverrun;
  }

  const uint8_t* inp = src;
  const uint8_t* inp_end = src + src_size;
  uint8_t* outp = dst;
  uint8_t* outp_end = dst + dst_size;
  uint8_t* lbcur;
  std::size_t lblen;
  std::size_t state = 0;
  std::size_t nstate = 0;

  /* First byte encoding */
  if (*inp >= 22) {
    /* 22..255 : copy literal string
     *           length = (byte - 17) = 4..238
     *           state = 4 [ don't copy extra literals ]
     *           skip byte
     */
    std::size_t len = *inp++ - uint8_t(17);
    NEEDS_IN(len)
    NEEDS_OUT(len)
    for (std::size_t i = 0; i < len; ++i)
      *outp++ = *inp++;
    state = 4;
  } else if (*inp >= 18) {
    /* 18..21 : copy 0..3 literals
     *          state = (byte - 17) = 0..3  [ copy <state> literals ]
     *          skip byte
     */
    nstate = *inp++ - uint8_t(17);
    state = nstate;
    NEEDS_IN(nstate)
    NEEDS_OUT(nstate)
    for (std::size_t i = 0; i < nstate; ++i)
      *outp++ = *inp++;
  }
  /* 0..17 : follow regular instruction encoding, see below. It is worth
   *         noting that codes 16 and 17 will represent a block copy from
   *         the dictionary which is empty, and that they will always be
   *         invalid at this place.
   */

  while (true) {
    NEEDS_IN(1)
    uint8_t inst = *inp++;
    if (inst & 0xC0) {
      /* [M2]
       * 1 L L D D D S S  (128..255)
       *   Copy 5-8 bytes from block within 2kB distance
       *   state = S (copy S literals after this block)
       *   length = 5 + L
       * Always followed by exactly one byte : H H H H H H H H
       *   distance = (H << 3) + D + 1
       *
       * 0 1 L D D D S S  (64..127)
       *   Copy 3-4 bytes from block within 2kB distance
       *   state = S (copy S literals after this block)
       *   length = 3 + L
       * Always followed by exactly one byte : H H H H H H H H
       *   distance = (H << 3) + D + 1
       */
      NEEDS_IN(1)
      lbcur = outp - ((*inp++ << 3) + ((inst >> 2) & 0x7) + 1);
      lblen = std::size_t(inst >> 5) + 1;
      nstate = inst & uint8_t(0x3);
    } else if (inst & M3Marker) {
      /* [M3]
       * 0 0 1 L L L L L  (32..63)
       *   Copy of small block within 16kB distance (preferably less than 34B)
       *   length = 2 + (L ?: 31 + (zero_bytes * 255) + non_zero_byte)
       * Always followed by exactly one LE16 :  D D D D D D D D : D D D D D D S S
       *   distance = D + 1
       *   state = S (copy S literals after this block)
       */
      lblen = std::size_t(inst & uint8_t(0x1f)) + 2;
      if (lblen == 2) {
        CONSUME_ZERO_BYTE_LENGTH
        NEEDS_IN(1)
        lblen += offset * 255 + 31 + *inp++;
      }
      NEEDS_IN(2)
      nstate = get_le16(inp);
      inp += 2;
      lbcur = outp - ((nstate >> 2) + 1);
      nstate &= 0x3;
    } else if (inst & M4Marker) {
      /* [M4]
       * 0 0 0 1 H L L L  (16..31)
       *   Copy of a block within 16..48kB distance (preferably less than 10B)
       *   length = 2 + (L ?: 7 + (zero_bytes * 255) + non_zero_byte)
       * Always followed by exactly one LE16 :  D D D D D D D D : D D D D D D S S
       *   distance = 16384 + (H << 14) + D
       *   state = S (copy S literals after this block)
       *   End of stream is reached if distance == 16384
       */
      lblen = std::size_t(inst & uint8_t(0x7)) + 2;
      if (lblen == 2) {
        CONSUME_ZERO_BYTE_LENGTH
        NEEDS_IN(1)
        lblen += offset * 255 + 7 + *inp++;
      }
      NEEDS_IN(2)
      nstate = get_le16(inp);
      inp += 2;
      lbcur = outp - (((inst & 0x8) << 11) + (nstate >> 2));
      nstate &= 0x3;
      if (lbcur == outp)
        break; /* Stream finished */
      lbcur -= 16384;
    } else {
      /* [M1] Depends on the number of literals copied by the last instruction. */
      if (state == 0) {
        /* If last instruction did not copy any literal (state == 0), this
         * encoding will be a copy of 4 or more literal, and must be interpreted
         * like this :
         *
         *    0 0 0 0 L L L L  (0..15)  : copy long literal string
         *    length = 3 + (L ?: 15 + (zero_bytes * 255) + non_zero_byte)
         *    state = 4  (no extra literals are copied)
         */
        std::size_t len = inst + 3;
        if (len == 3) {
          CONSUME_ZERO_BYTE_LENGTH
          NEEDS_IN(1)
          len += offset * 255 + 15 + *inp++;
        }
        /* copy_literal_run */
        NEEDS_IN(len)
        NEEDS_OUT(len)
        for (std::size_t i = 0; i < len; ++i)
          *outp++ = *inp++;
        state = 4;
        continue;
      } else if (state != 4) {
        /* If last instruction used to copy between 1 to 3 literals (encoded in
         * the instruction's opcode or distance), the instruction is a copy of a
         * 2-byte block from the dictionary within a 1kB distance. It is worth
         * noting that this instruction provides little savings since it uses 2
         * bytes to encode a copy of 2 other bytes but it encodes the number of
         * following literals for free. It must be interpreted like this :
         *
         *    0 0 0 0 D D S S  (0..15)  : copy 2 bytes from <= 1kB distance
         *    length = 2
         *    state = S (copy S literals after this block)
         *  Always followed by exactly one byte : H H H H H H H H
         *    distance = (H << 2) + D + 1
         */
        NEEDS_IN(1)
        nstate = inst & uint8_t(0x3);
        lbcur = outp - ((inst >> 2) + (*inp++ << 2) + 1);
        lblen = 2;
      } else {
        /* If last instruction used to copy 4 or more literals (as detected by
         * state == 4), the instruction becomes a copy of a 3-byte block from the
         * dictionary from a 2..3kB distance, and must be interpreted like this :
         *
         *    0 0 0 0 D D S S  (0..15)  : copy 3 bytes from 2..3 kB distance
         *    length = 3
         *    state = S (copy S literals after this block)
         *  Always followed by exactly one byte : H H H H H H H H
         *    distance = (H << 2) + D + 2049
         */
        NEEDS_IN(1)
        nstate = inst & uint8_t(0x3);
        lbcur = outp - ((inst >> 2) + (*inp++ << 2) + 2049);
        lblen = 3;
      }
    }
    if (lbcur < dst) {
      dst_size = outp - dst;
      return EResult::LookbehindOverrun;
    }
    NEEDS_IN(nstate)
    NEEDS_OUT(lblen + nstate)
    /* Copy lookbehind */
    for (std::size_t i = 0; i < lblen; ++i)
      *outp++ = *lbcur++;
    state = nstate;
    /* Copy literal */
    for (std::size_t i = 0; i < nstate; ++i)
      *outp++ = *inp++;
  }

  dst_size = outp - dst;
  if (lblen != 3) /* Ensure terminating M4 was encountered */
    return EResult::Error;
  if (inp == inp_end)
    return EResult::Success;
  else if (inp < inp_end)
    return EResult::InputNotConsumed;
  else
    return EResult::InputOverrun;
}

struct State {
  const uint8_t* src;
  const uint8_t* src_end;
  const uint8_t* inp;
  uint32_t wind_sz;
  uint32_t wind_b;
  uint32_t wind_e;
  uint32_t cycle1_countdown;

  const uint8_t* bufp;
  uint32_t buf_sz;

  /* Access next input byte and advance both ends of circular buffer */
  void get_byte(uint8_t* buf) {
    if (inp >= src_end) {
      if (wind_sz > 0)
        --wind_sz;
      buf[wind_e] = 0;
      if (wind_e < DictBase::MaxMatchLen)
        buf[DictBase::BufSize + wind_e] = 0;
    } else {
      buf[wind_e] = *inp;
      if (wind_e < DictBase::MaxMatchLen)
        buf[DictBase::BufSize + wind_e] = *inp;
      ++inp;
    }
    if (++wind_e == DictBase::BufSize)
      wind_e = 0;
    if (++wind_b == DictBase::BufSize)
      wind_b = 0;
  }

  uint32_t pos2off(uint32_t pos) const {
    return wind_b > pos ? wind_b - pos : DictBase::BufSize - (pos - wind_b);
  }
};

class DictImpl : public DictBase {
public:
  struct Match3Impl : DictBase::Match3 {
    static uint32_t make_key(const uint8_t* data) {
      return ((0x9f5f * (((uint32_t(data[0]) << 5 ^ uint32_t(data[1])) << 5) ^ data[2])) >> 5) & 0x3fff;
    }

    uint16_t get_head(uint32_t key) const {
      return (chain_sz[key] == 0) ? uint16_t(UINT16_MAX) : head[key];
    }

    void init() {
      std::fill(chain_sz.begin(), chain_sz.end(), 0);
    }

    void remove(uint32_t pos, const uint8_t* b) {
      --chain_sz[make_key(b + pos)];
    }

    void advance(State& s, uint32_t& match_pos, uint32_t& match_count, const uint8_t* b) {
      uint32_t key = make_key(b + s.wind_b);
      match_pos = chain[s.wind_b] = get_head(key);
      match_count = chain_sz[key]++;
      if (match_count > DictBase::MaxMatchLen)
        match_count = DictBase::MaxMatchLen;
      head[key] = uint16_t(s.wind_b);
    }

    void skip_advance(State& s, const uint8_t* b) {
      uint32_t key = make_key(b + s.wind_b);
      chain[s.wind_b] = get_head(key);
      head[key] = uint16_t(s.wind_b);
      best_len[s.wind_b] = uint16_t(DictBase::MaxMatchLen + 1);
      chain_sz[key]++;
    }
  };

  struct Match2Impl : DictBase::Match2 {
    static uint32_t make_key(const uint8_t* data) {
      return uint32_t(data[0]) ^ (uint32_t(data[1]) << 8);
    }

    void init() {
      std::fill(std::begin(head), std::end(head), UINT16_MAX);
    }

    void add(uint16_t pos, const uint8_t* b) {
      head[make_key(b + pos)] = pos;
    }

    void remove(uint32_t pos, const uint8_t* b) {
      uint16_t& p = head[make_key(b + pos)];
      if (p == pos)
        p = UINT16_MAX;
    }

    bool search(State& s, uint32_t& lb_pos, uint32_t& lb_len,
                uint32_t best_pos[MaxMatchByLengthLen], const uint8_t* b) const {
      uint16_t pos = head[make_key(b + s.wind_b)];
      if (pos == UINT16_MAX)
        return false;
      if (best_pos[2] == 0)
        best_pos[2] = pos + 1;
      if (lb_len < 2) {
        lb_len = 2;
        lb_pos = pos;
      }
      return true;
    }
  };

  void init(State& s, const uint8_t* src, std::size_t src_size) {
    auto& match3 = static_cast<Match3Impl&>(_storage->match3);
    auto& match2 = static_cast<Match2Impl&>(_storage->match2);

    s.cycle1_countdown = DictBase::MaxDist;
    match3.init();
    match2.init();

    s.src = src;
    s.src_end = src + src_size;
    s.inp = src;
    s.wind_sz = uint32_t(std::min(src_size, std::size_t(MaxMatchLen)));
    s.wind_b = 0;
    s.wind_e = s.wind_sz;
    std::copy_n(s.inp, s.wind_sz, _storage->buffer.data());
    s.inp += s.wind_sz;

    if (s.wind_e == DictBase::BufSize)
      s.wind_e = 0;

    if (s.wind_sz < 3)
      std::fill_n(_storage->buffer.data() + s.wind_b + s.wind_sz, 3, 0);
  }

  void reset_next_input_entry(State& s, Match3Impl& match3, Match2Impl& match2) {
    /* Remove match from about-to-be-clobbered buffer entry */
    if (s.cycle1_countdown == 0) {
      match3.remove(s.wind_e, _storage->buffer.data());
      match2.remove(s.wind_e, _storage->buffer.data());
    } else {
      --s.cycle1_countdown;
    }
  }

  void advance(State& s, uint32_t& lb_off, uint32_t& lb_len,
               uint32_t best_off[MaxMatchByLengthLen], bool skip) {
    auto& match3 = static_cast<Match3Impl&>(_storage->match3);
    auto& match2 = static_cast<Match2Impl&>(_storage->match2);

    if (skip) {
      for (uint32_t i = 0; i < lb_len - 1; ++i) {
        reset_next_input_entry(s, match3, match2);
        match3.skip_advance(s, _storage->buffer.data());
        match2.add(uint16_t(s.wind_b), _storage->buffer.data());
        s.get_byte(_storage->buffer.data());
      }
    }

    lb_len = 1;
    lb_off = 0;
    uint32_t lb_pos;

    uint32_t best_pos[MaxMatchByLengthLen] = {};
    uint32_t match_pos, match_count;
    match3.advance(s, match_pos, match_count, _storage->buffer.data());

    int best_char = _storage->buffer[s.wind_b];
    uint32_t best_len = lb_len;
    if (lb_len >= s.wind_sz) {
      if (s.wind_sz == 0)
        best_char = -1;
      lb_off = 0;
      match3.best_len[s.wind_b] = DictBase::MaxMatchLen + 1;
    } else {
      if (match2.search(s, lb_pos, lb_len, best_pos, _storage->buffer.data()) && s.wind_sz >= 3) {
        for (uint32_t i = 0; i < match_count; ++i, match_pos = match3.chain[match_pos]) {
          auto ref_ptr = _storage->buffer.data() + s.wind_b;
          auto match_ptr = _storage->buffer.data() + match_pos;
          auto mismatch = std::mismatch(ref_ptr, ref_ptr + s.wind_sz, match_ptr);
          auto match_len = uint32_t(mismatch.first - ref_ptr);
          if (match_len < 2)
            continue;
          if (match_len < MaxMatchByLengthLen && best_pos[match_len] == 0)
            best_pos[match_len] = match_pos + 1;
          if (match_len > lb_len) {
            lb_len = match_len;
            lb_pos = match_pos;
            if (match_len == s.wind_sz || match_len > match3.best_len[match_pos])
              break;
          }
        }
      }
      if (lb_len > best_len)
        lb_off = s.pos2off(lb_pos);
      match3.best_len[s.wind_b] = uint16_t(lb_len);
      for (auto posit = std::begin(best_pos) + 2, offit = best_off + 2;
           posit != std::end(best_pos); ++posit, ++offit) {
        *offit = (*posit > 0) ? s.pos2off(*posit - 1) : 0;
      }
    }

    reset_next_input_entry(s, match3, match2);

    match2.add(uint16_t(s.wind_b), _storage->buffer.data());

    s.get_byte(_storage->buffer.data());

    if (best_char < 0) {
      s.buf_sz = 0;
      lb_len = 0;
      /* Signal exit */
    } else {
      s.buf_sz = s.wind_sz + 1;
    }
    s.bufp = s.inp - s.buf_sz;
  }
};

static void find_better_match(const uint32_t best_off[MaxMatchByLengthLen], uint32_t& lb_len, uint32_t& lb_off) {
  if (lb_len <= M2MinLen || lb_off <= M2MaxOffset)
    return;
  if (lb_off > M2MaxOffset && lb_len >= M2MinLen + 1 && lb_len <= M2MaxLen + 1 &&
      best_off[lb_len - 1] != 0 && best_off[lb_len - 1] <= M2MaxOffset) {
    lb_len -= 1;
    lb_off = best_off[lb_len];
  } else if (lb_off > M3MaxOffset && lb_len >= M4MaxLen + 1 && lb_len <= M2MaxLen + 2 &&
             best_off[lb_len - 2] && best_off[lb_len] <= M2MaxOffset) {
    lb_len -= 2;
    lb_off = best_off[lb_len];
  } else if (lb_off > M3MaxOffset && lb_len >= M4MaxLen + 1 && lb_len <= M3MaxLen + 1 &&
             best_off[lb_len - 1] != 0 && best_off[lb_len - 2] <= M3MaxOffset) {
    lb_len -= 1;
    lb_off = best_off[lb_len];
  }
}

static EResult encode_literal_run(uint8_t*& outp, const uint8_t* outp_end, const uint8_t* dst, std::size_t& dst_size,
                                  const uint8_t* lit_ptr, uint32_t lit_len) {
  if (outp == dst && lit_len <= 238) {
    NEEDS_OUT(1);
    *outp++ = uint8_t(17 + lit_len);
  } else if (lit_len <= 3) {
    outp[-2] = uint8_t(outp[-2] | lit_len);
  } else if (lit_len <= 18) {
    NEEDS_OUT(1);
    *outp++ = uint8_t(lit_len - 3);
  } else {
    NEEDS_OUT((lit_len - 18) / 255 + 2);
    *outp++ = 0;
    WRITE_ZERO_BYTE_LENGTH(lit_len - 18);
  }
  NEEDS_OUT(lit_len);
  outp = std::copy_n(lit_ptr, lit_len, outp);
  return EResult::Success;
}

static EResult encode_lookback_match(uint8_t*& outp, const uint8_t* outp_end, const uint8_t* dst, std::size_t& dst_size,
                                     uint32_t lb_len, uint32_t lb_off, uint32_t last_lit_len) {
  if (lb_len == 2) {
    lb_off -= 1;
    NEEDS_OUT(2);
    *outp++ = uint8_t(M1Marker | ((lb_off & 0x3) << 2));
    *outp++ = uint8_t(lb_off >> 2);
  } else if (lb_len <= M2MaxLen && lb_off <= M2MaxOffset) {
    lb_off -= 1;
    NEEDS_OUT(2);
    *outp++ = uint8_t((lb_len - 1) << 5 | ((lb_off & 0x7) << 2));
    *outp++ = uint8_t(lb_off >> 3);
  } else if (lb_len == M2MinLen && lb_off <= M1MaxOffset + M2MaxOffset && last_lit_len >= 4) {
    lb_off -= 1 + M2MaxOffset;
    NEEDS_OUT(2);
    *outp++ = uint8_t(M1Marker | ((lb_off & 0x3) << 2));
    *outp++ = uint8_t(lb_off >> 2);
  } else if (lb_off <= M3MaxOffset) {
    lb_off -= 1;
    if (lb_len <= M3MaxLen) {
      NEEDS_OUT(1);
      *outp++ = uint8_t(M3Marker | (lb_len - 2));
    } else {
      lb_len -= M3MaxLen;
      NEEDS_OUT(lb_len / 255 + 2);
      *outp++ = uint8_t(M3Marker);
      WRITE_ZERO_BYTE_LENGTH(lb_len);
    }
    NEEDS_OUT(2);
    *outp++ = uint8_t(lb_off << 2);
    *outp++ = uint8_t(lb_off >> 6);
  } else {
    lb_off -= 0x4000;
    if (lb_len <= M4MaxLen) {
      NEEDS_OUT(1);
      *outp++ = uint8_t(M4Marker | ((lb_off & 0x4000) >> 11) | (lb_len - 2));
    } else {
      lb_len -= M4MaxLen;
      NEEDS_OUT(lb_len / 255 + 2);
      *outp++ = uint8_t(M4Marker | ((lb_off & 0x4000) >> 11));
      WRITE_ZERO_BYTE_LENGTH(lb_len);
    }
    NEEDS_OUT(2);
    *outp++ = uint8_t(lb_off << 2);
    *outp++ = uint8_t(lb_off >> 6);
  }
  return EResult::Success;
}

EResult compress(const uint8_t* src, std::size_t src_size,
                 uint8_t* dst, std::size_t init_dst_size,
                 std::size_t& dst_size, DictBase& dict) {
  EResult err;
  State s;
  auto& d = static_cast<DictImpl&>(dict);
  dst_size = init_dst_size;
  uint8_t* outp = dst;
  uint8_t* outp_end = dst + dst_size;
  uint32_t lit_len = 0;
  uint32_t lb_off, lb_len;
  uint32_t best_off[MaxMatchByLengthLen];
  d.init(s, src, src_size);
  const uint8_t* lit_ptr = s.inp;
  d.advance(s, lb_off, lb_len, best_off, false);
  while (s.buf_sz > 0) {
    if (lit_len == 0)
      lit_ptr = s.bufp;
    if (lb_len < 2 || (lb_len == 2 && (lb_off > M1MaxOffset || lit_len == 0 || lit_len >= 4)) ||
        (lb_len == 2 && outp == dst) || (outp == dst && lit_len == 0)) {
      lb_len = 0;
    } else if (lb_len == M2MinLen && lb_off > M1MaxOffset + M2MaxOffset && lit_len >= 4) {
      lb_len = 0;
    }
    if (lb_len == 0) {
      ++lit_len;
      d.advance(s, lb_off, lb_len, best_off, false);
      continue;
    }
    find_better_match(best_off, lb_len, lb_off);
    if ((err = encode_literal_run(outp, outp_end, dst, dst_size, lit_ptr, lit_len)) < EResult::Success)
      return err;
    if ((err = encode_lookback_match(outp, outp_end, dst, dst_size, lb_len, lb_off, lit_len)) < EResult::Success)
      return err;
    lit_len = 0;
    d.advance(s, lb_off, lb_len, best_off, true);
  }
  if ((err = encode_literal_run(outp, outp_end, dst, dst_size, lit_ptr, lit_len)) < EResult::Success)
    return err;

  /* Terminating M4 */
  NEEDS_OUT(3);
  *outp++ = M4Marker | 1;
  *outp++ = 0;
  *outp++ = 0;

  dst_size = outp - dst;
  return EResult::Success;
}

}