using Invercargill.DataStructures; using Invercargill.Modifiers; using Invercargill.Mapping; namespace Invercargill { public abstract class Enumerable : Object { public abstract Tracker get_tracker(); public abstract int? peek_count(); public abstract EnumerableInfo get_info(); // Returns false if iteration was interrupted // Returns true if iteration reached natural end public virtual bool iterate_if(PredicateDelegate handler) { var tracker = get_tracker(); while(tracker.has_next()) { if(!handler(tracker.get_next())) { return false; } } return true; } public virtual void iterate(ItemDelegate? handler = null) { if(handler != null) { iterate_if(i => { handler(i); return true; }); return; } iterate_if(i => true); } public virtual Series to_series() { var series = new Series(); iterate(i => series.add(i)); return series; } public virtual Gee.Collection to_gee_collection() { var collection = new Gee.LinkedList(); iterate(i => collection.add(i)); return collection; } public virtual Tracker iterator() { return get_tracker(); } public virtual T[] to_array() { var array = new T[1024]; var index = 0; foreach (var item in this) { if(index >= array.length) { array.resize(array.length*2); } safely_assign_to_array(array, index, item); index++; } array.resize(index); return array; } public virtual int count(PredicateDelegate? predicate = null) { var enumerable = this; // When no predicate is passed, see if we can peek at the count of // the enumerable before iterating. if(predicate == null) { var peek = peek_count(); if(peek != null) { return peek; } } else { enumerable = this.where(predicate); } var count = 0; enumerable.iterate(i => count++); return count; } public virtual bool any(PredicateDelegate? predicate = null) { var result = false; var p = resolve_nullable_predicate(predicate); iterate_if(i => { if(p(i)) { result = true; return false; } return true; }); return result; } public virtual bool all(PredicateDelegate predicate) { return !any(i => !predicate(i)); } public virtual bool no(PredicateDelegate? predicate = null) { var p = resolve_nullable_predicate(predicate); return all(i => !p(i)); } public virtual Enumerable where(owned PredicateDelegate predicate) { return new Filter(this, (owned)predicate); } public virtual Enumerable from(owned PredicateDelegate predicate) { return new From(this, (owned)predicate); } public virtual Enumerable until(owned PredicateDelegate predicate) { return new Until(this, (owned)predicate); } public virtual Enumerable select_where(owned FilterTransformDelegate transform) { return new FilterTransform(this, (owned)transform); } public virtual Enumerable select(owned TransformDelegate transform) { return new Transform(this, (owned)transform); } public virtual Attempts attempt_select(owned AttemptTransformDelegate transform) { return select>(i => new Attempt(() => transform(i))).assert_promotion(); } public virtual Attempts attempt_select_nested(owned AttemptTransformDelegate>> transform) { return attempt_select>>((owned)transform) .as_enumerable() .select_many>(a => a.success ? a.result : Invercargill.single>(new Attempt.unsuccessful(a.error))) .assert_promotion(); } public virtual Enumerable> select_pairs(owned TransformDelegate transform1, owned TransformDelegate transform2) { return select>(i => new Pair(transform1(i), true, transform2(i), true)); } public virtual Enumerable select_many(owned TransformDelegate> transform) { return new MergeQuery(select((owned)transform)); } public virtual Enumerable sort(owned CompareDelegate compare) { return new Sort(this, (owned)compare); } public virtual Enumerable concat(Enumerable other) { return new Concat(this, other); } public virtual Enumerable take(int count) { return new Take(this, count); } public virtual Enumerable skip(int count) { return new Skip(this, count); } public virtual Enumerable cast() { return select(i => (Tout)i); } public virtual Enumerable parallel_select(owned TransformDelegate transform, uint workers = 0) { var actual_workers = workers; if(actual_workers < 1) { actual_workers = get_num_processors(); } return new Parallel(this, (owned)transform, (int)actual_workers); } public virtual void parallel_iterate(ItemDelegate handler, uint workers = 0) { parallel_select(i => { handler(i); return i; }, workers) .iterate(); } public virtual Enumerable> contextualised_select(owned TransformDelegate transform) { return select>((i) => new SelectionContext() { origin = i, result = transform(i) }); } public virtual Tout aggregate(Tout initial, AggregateDelegate aggregate_func) { var aggregate = initial; iterate(i => { aggregate = aggregate_func(aggregate, i); }); return aggregate; } public virtual T max(TransformDelegate int_delegate) { T item = null; var first = true; var value = 0; foreach (var i in this) { if(first) { first = false; item = i; value = int_delegate(i); continue; } var item_value = int_delegate(i); if(item_value > value) { value = item_value; item = i; } } return item; } public virtual T min(TransformDelegate int_delegate) { T item = null; var first = true; var value = 0; foreach (var i in this) { if(first) { first = false; item = i; value = int_delegate(i); continue; } var item_value = int_delegate(i); if(item_value < value) { value = item_value; item = i; } } return item; } public virtual bool contains(T item, EqualityDelegate? equator = null) { var func = equator ?? Operators.equality(); return any(i => func(i, item)); } public virtual Enumerable zip(Enumerable other, owned ZipperTransformDelegate transform) { return new Zip(this, other, (owned)transform); } public virtual Enumerable> pair_up(Enumerable other) { return zip>(other, (t1v, t1vs, t2v, t2vs) => new Pair(t1v, t1vs, t2v, t2vs)); } public virtual Enumerable interleave(Enumerable other) { return new Interleave(this, other); } public virtual Enumerable fork(owned TransformDelegate fork1, owned TransformDelegate fork2) { var seq = to_series(); return seq.select((owned)fork1).interleave(seq.select((owned)fork2)); } public virtual Enumerable fork_many(owned TransformDelegate> fork1, owned TransformDelegate> fork2) { return new MergeQuery(fork((owned)fork1, (owned)fork2)); } public virtual bool matches(Enumerable other, EqualityDelegate equals) { return zip(other, (t1v, t1vs, t2v, t2vs) => t1vs == t2vs && equals(t1v, t2v)).all(r => r); } public virtual Enumerable act(ItemDelegate handler) { return select(i => { handler(i); return i; }); } public virtual Enumerable distinct(owned EqualityDelegate? comparison = null) { return distinct_by(i => i, (owned)comparison); } public virtual Enumerable distinct_by(owned TransformDelegate property_selector, owned EqualityDelegate? property_equality) { var func = property_equality ?? Operators.equality(); return new Unique(this, (owned)property_selector, (owned)func); } public virtual Enumerable> group_by(owned TransformDelegate key_selector, owned EqualityDelegate? key_equality = null) { var equality = key_equality ?? Operators.equality(); var keys = select(i => key_selector(i)).distinct((a, b) => equality(a, b)); return keys.select>(g => new Grouping(g, this.where(i => equality(g, key_selector(i))))); } public virtual Enumerable @with(T item, uint times = 1) { return concat(range(0, (int)times, 1).select(i => item)); } public virtual T first(owned PredicateDelegate? predicate = null) throws SequenceError { var tracker = predicate == null ? get_tracker() : where((owned)predicate).get_tracker(); if(tracker.has_next()) { return tracker.get_next(); } throw new SequenceError.NO_ELEMENTS("The sequence contains no elements"); } public virtual T? first_or_default(owned PredicateDelegate? predicate = null) { var tracker = predicate == null ? get_tracker() : where((owned)predicate).get_tracker(); if(tracker.has_next()) { return tracker.get_next(); } return null; } public virtual T last(owned PredicateDelegate? predicate = null) throws SequenceError { var tracker = predicate == null ? get_tracker() : where((owned)predicate).get_tracker(); if(!tracker.has_next()) { throw new SequenceError.NO_ELEMENTS("The sequence contains no elements"); } while(true) { T item = tracker.get_next(); if(!tracker.has_next()) { return item; } } } public virtual T? last_or_default(owned PredicateDelegate? predicate = null) { var tracker = predicate == null ? get_tracker() : where((owned)predicate).get_tracker(); if(!tracker.has_next()) { return null; } while(true) { T item = tracker.get_next(); if(!tracker.has_next()) { return item; } } } public virtual T single(owned PredicateDelegate? predicate = null) throws SequenceError { var tracker = predicate == null ? get_tracker() : where((owned)predicate).get_tracker(); if(tracker.has_next()) { var item = tracker.get_next(); if(tracker.has_next()) { throw new SequenceError.MULTUPLE_ELEMENTS("The sequence contains more than one element"); } return item; } throw new SequenceError.NO_ELEMENTS("The sequence contains no elements"); } public virtual T single_or_default(owned PredicateDelegate? predicate = null) throws SequenceError { var tracker = predicate == null ? get_tracker() : where((owned)predicate).get_tracker(); if(tracker.has_next()) { var item = tracker.get_next(); if(tracker.has_next()) { throw new SequenceError.MULTUPLE_ELEMENTS("The sequence contains more than one element"); } return item; } return null; } public virtual string to_string(TransformDelegate stringifier, string seperator = "") { bool is_first = true; return aggregate("", (s, v) => { if(is_first) { is_first = false; return stringifier(v); } return s + seperator + stringifier(v); }); } public virtual Object[] to_object_array() throws SequenceError { if(!typeof(T).is_object()) { throw new SequenceError.INVALID_TYPE("Can only make an object array of an Enumerable where T is derrived from GLib.Object"); } return select(i => (Object)i).to_array(); } public virtual Vector to_vector() { var vector = new Vector(); vector.add_all(this); return vector; } public virtual Type element_type { get { return typeof(T); }} public virtual TPromotion promote_to() throws PromotionError { var type = typeof(TPromotion); if(get_type().is_a(type)) { // Don't promote if we are already target type return this; } resolve_promotion(ref type); if(!type.is_instantiatable()) { throw new PromotionError.INVALID_PROMOTION_TYPE(@"Promotion type $(type.name()) is not instansiatable."); } if(!type.is_a(typeof(Promotion))) { throw new PromotionError.INVALID_PROMOTION_TYPE(@"Promotion type $(type.name()) does not implement Invercargill.Promotion."); } if(!type.is_a(typeof(Enumerable)) && typeof(TPromotion) == type) { throw new PromotionError.INVALID_PROMOTION_TYPE(@"Non-resolved promotion type $(type.name()) does not inherit from Invercargill.Enumerable."); } var promotion = Object.new(type); if(!((Promotion)promotion).can_wrap(element_type)) { throw new PromotionError.INCOMPATIBLE_ELEMENT_TYPE(@"Enumerable has an element type of $(element_type.name()) which cannot be wrapped by $(type.name())"); } return ((Promotion)promotion).wrap(this); } public virtual TPromotion assert_promotion() { try { return promote_to(); } catch (PromotionError error) { var base_type = get_type(); var type = typeof(TPromotion); critical(@"Cannot promote type $(base_type.name()) to $(type.name()): $(error.message)"); assert_not_reached(); } } public virtual Enumerable seal() { if(this.get_type().is_a(typeof(Sealed))) { return this; } return new Sealed(this); } public virtual Enumerable cache() { if(this.get_type().is_a(typeof(Cache))) { return this; } return new Cache(this); } public virtual Dictionary to_dictionary(TransformDelegate key_selecter, HashDelegate? key_hash_func = null, EqualityDelegate? key_equal_func = null) { var dict = new Dictionary(key_hash_func, key_equal_func); iterate(i => dict.add(key_selecter(i), i)); return dict; } public virtual Dictionary select_to_dictionary(TransformDelegate key_selecter, TransformDelegate value_selecter, HashDelegate? key_hash_func = null, EqualityDelegate? key_equal_func = null) { var dict = new Dictionary(key_hash_func, key_equal_func); iterate(i => dict.add(key_selecter(i), value_selecter(i))); return dict; } public virtual HashSet to_hash_set(HashDelegate? hash_func = null, EqualityDelegate? equal_func = null) { var @set = new HashSet(hash_func, equal_func); @set.union_with(this); return @set; } public virtual Enumerable> with_positions() { return new Position(this); } public virtual Elements to_elements() { var series = new ElementSeries(); if(typeof(T).is_a(typeof(Element))) { series.add_all((Enumerable)this); } else { series.add_all(select(i => new NativeElement(i))); } return series; } public virtual Attempts attempt_map_with(Mapper mapper) { return attempt_select(o => mapper.materialise(o)); } public virtual Enumerable as_enumerable() { return this; } public virtual void debug_dump(TransformDelegate stringifier, string additional_message = "", DebugOutputDelegate? output_func = null, bool formatting = true) { DebugPrinter.print_enumerable_dump(this, stringifier, additional_message, output_func, formatting); } public virtual Enumerable debug_type(string additional_message = "", DebugOutputDelegate? output_func = null, bool formatting = true) { DebugPrinter.print_enumerable_information(get_info(), additional_message, output_func, formatting); return this; } public virtual Enumerable debug_trace(owned TransformDelegate stringifier, string additional_message = "", owned DebugOutputDelegate? output_func = null, bool formatting = true) { return new TraceEnumerable(this, get_info(), (owned)stringifier, additional_message, (owned)output_func, formatting); } private PredicateDelegate resolve_nullable_predicate(PredicateDelegate? predicate) { if(predicate == null) { return (p) => true; } return predicate; } } }