/*
 * tkRangeList.h --
 *
 *	This module implements operations on a list of integer ranges.
 *	Note that the current implementation expects short lists of
 *	ranges, it is quite slow for large list sizes (large number of
 *	range items).
 *
 * Copyright (c) 2015-2017 Gregor Cramer
 *
 * See the file "license.terms" for information on usage and redistribution of
 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
 */

#ifndef _TKRANGELIST
#define _TKRANGELIST

#include "tkBool.h"

#if defined(__GNUC__) || defined(__clang__)
# define __warn_unused__ __attribute__((warn_unused_result))
#else
# define __warn_unused__
#endif

#ifdef _MSC_VER
# if _MSC_VER >= 1900
#  define inline __inline
# else
#  define inline
# endif
#elif __STDC_VERSION__ < 199901L
# define inline /* we are not C99 conform */
#endif


typedef struct TkRange {
    int low;
    int high;
} TkRange;

/*
 * Return the span of given range.
 */
inline int TkRangeSpan(const TkRange *range);

/*
 * Test whether given range contains the specified value.
 */
inline bool TkRangeTest(const TkRange *range, int value);


typedef struct TkRangeList {
    unsigned size;
    unsigned capacity;
    unsigned count;
    TkRange items[1];
} TkRangeList;

/*
 * Create a range list, and reserve some space for range entries.
 */
TkRangeList *TkRangeListCreate(unsigned capacity) __warn_unused__;

/*
 * Make a copy of given list.
 */
TkRangeList *TkRangeListCopy(const TkRangeList *ranges) __warn_unused__;

/*
 * Destroy the range list, the derefenced pointer can be NULL (in this case the
 * list is already destroyed).
 */
void TkRangeListDestroy(TkRangeList **rangesPtr);

/*
 * Clear the given list of ranges.
 */
void TkRangeListClear(TkRangeList *ranges);

/*
 * Truncate this list at front until given value (inclusive). This means that after
 * truncation the lowest value in this list will be larger than 'untilThisValue'.
 */
void TkRangeListTruncateAtFront(TkRangeList *ranges, int untilThisValue);

/*
 * Truncate this list at end with given value (exclusive). This means that after
 * truncation the highest value in this list will be less or equal to 'maxValue'.
 */
void TkRangeListTruncateAtEnd(TkRangeList *ranges, int maxValue);

/*
 * Return the lower value of the entry with lowest order (ths lowest value inside
 * the whole list).
 */
inline int TkRangeListLow(const TkRangeList *ranges);

/*
 * Return the upper value of the entry with highest order (ths highest value inside
 * the whole list).
 */
inline int TkRangeListHigh(const TkRangeList *ranges);

/*
 * Return the span of the whole list (= TkRangeListHigh(ranges) - TkRangeListLow(ranges) + 1).
 */
inline unsigned TkRangeListSpan(const TkRangeList *ranges);

/*
 * Return the number of integers contained in this list.
 */
inline unsigned TkRangeListCount(const TkRangeList *ranges);

/*
 * Return the number of entries (pairs) in this list.
 */
inline unsigned TkRangeListSize(const TkRangeList *ranges);

/*
 * Return a specific entry (pair), the index must not exceed the size of this list.
 */
inline const TkRange *TkRangeListAccess(const TkRangeList *ranges, unsigned index);

/*
 * Find entry (pair) which contains the given value. NULL will be returned if
 * this value is not contained in this list.
 */
const TkRange *TkRangeListFind(const TkRangeList *ranges, int value);

/*
 * Find entry (pair) which contains the given value. If this value is not contained
 * in given list then return the item with a low value nearest to specified value.
 * But it never returns an item with a high value less than given value, so it's
 * possible that NULL we returned.
 */
const TkRange *TkRangeListFindNearest(const TkRangeList *ranges, int value);

/*
 * Return the first item in given list, can be NULL if list is empty.
 */
inline const TkRange *TkRangeListFirst(const TkRangeList *ranges);

/*
 * Return the next item in given list, can be NULL if at end of list.
 */
inline const TkRange *TkRangeListNext(const TkRangeList *ranges, const TkRange *item);

/*
 * Return whether this list is empty.
 */
inline bool TkRangeListIsEmpty(const TkRangeList *ranges);

/*
 * Return whether the given value is contained in this list.
 */
inline bool TkRangeListContains(const TkRangeList *ranges, int value);

/*
 * Return whether the given range is contained in this list.
 */
inline bool TkRangeListContainsRange(const TkRangeList *ranges, int low, int high);

/*
 * Return whether any value of the given range is contained in this list.
 */
bool TkRangeListContainsAny(const TkRangeList *ranges, int low, int high);

/*
 * Add given range to this list. Adjacent entries (pairs) will be amalgamated
 * automatically.
 */
TkRangeList *TkRangeListAdd(TkRangeList *ranges, int low, int high) __warn_unused__;

/*
 * Remove given range from list. Adjacent entries (pairs) will be amalgamated
 * automatically.
 */
TkRangeList *TkRangeListRemove(TkRangeList *ranges, int low, int high);

/*
 * Insert given range to this list. This method has the side effect that all contained
 * integers with a value higher than the 'high' value will be increased by the span of
 * the given range (high - low + 1). Adjacent entries (pairs) will be amalgamated
 * automatically.
 *
 * Example: TkRangeListInsert({{5,6} {8,9}}, 1, 5) -> {{1,5} {10,11} {13,14}}
 */
TkRangeList *TkRangeListInsert(TkRangeList *ranges, int low, int high) __warn_unused__;

/*
 * Delete given range from list. This method has the side effect that all contained
 * integers with a value higher than the 'low' value will be decreased by the span of
 * the given range (high - low + 1). Adjacent entries (pairs) will be amalgamated
 * automatically.
 *
 * Example: TkRangeListDelete({{5,6} {8,9}}, 1, 5) -> {{1} {3,4}}
 */
TkRangeList *TkRangeListDelete(TkRangeList *ranges, int low, int high);

#if !NDEBUG
void TkRangeListPrint(const TkRangeList *ranges);
#endif


#if __STDC_VERSION__ >= 199901L || (defined(_MSC_VER) && _MSC_VER >= 1900)
# define _TK_NEED_IMPLEMENTATION
# include "tkRangeListPriv.h"
# undef _TK_NEED_IMPLEMENTATION
#else
# undef inline
#endif

#endif /* _TKRANGELIST */
/* vi:set ts=8 sw=4: */