namespace ConsoleApplication22
using System.Collections.Generic;
using System.Collections;
using static System.Console;
public static void Main(string[] args)
var loyal = Set<Customer>(с => с.Invoiced() > 10000);
var debtors = Set<Customer>(c => c.Balance() > 0);
var creditable = Set<Customer>(c => c.Balance() < 5000) & loyal;
var bulk = Set<Customer>(c => c.Orders.Any(o => o.Total > 2000));
var tenOff = bulk & loyal & !debtors;
var fiveOff = bulk & loyal & creditable - tenOff;
var noOff = !tenOff & !fiveOff;
foreach (var c in Repository.Customers.Intersect(fiveOff))
WriteLine($"-5%: {c.Name}");
foreach (var c in Repository.Customers.Intersect(tenOff))
WriteLine($"-10%: {c.Name}");
foreach (var c in Repository.Customers.Intersect(noOff))
WriteLine($"0%: {c.Name}");
public string Name { get; set; }
public List<Order> Orders { get; set; }
public List<Invoice> Invoices { get; set; }
public decimal Total { get; set; }
public decimal Total { get; set; }
public static decimal Invoiced(this Customer customer) =>
customer.Invoices.Sum(i => i.Total);
public static decimal Ordered(this Customer customer) =>
customer.Orders.Sum(o => o.Total);
public static decimal Balance(this Customer customer) =>
customer.Ordered() - customer.Invoiced();
public static IEnumerable<Customer> Customers => new[]
Name = "Tom the loyal debitor",
new Order { Total = 10000 },
new Order { Total = 20000 },
new Order { Total = 40000 },
new Order { Total = 10000 },
Invoices = new List<Invoice>
new Invoice { Total = 10000 },
new Invoice { Total = 20000 }
new Order { Total = 10000 },
new Order { Total = 20000 },
Invoices = new List<Invoice>
new Invoice { Total = 10000 },
new Invoice { Total = 20000 }
public static Set<T> Set<T>() => Set<T>(i => true);
public static Set<T> Set<T>(Predicate<T> predicate) => new Set<T>(predicate);
public static Enumerable<T> Intersect<T>(this IEnumerable<T> source, Set<T> set) =>
public static implicit operator Set<T>(T value) =>
public Set(params T[] values)
: this(values.AsEnumerable())
public Set(IEnumerable<T> values)
: this(i => values.Contains(i))
public Set(Predicate<T> predicate)
public Enumerable<T> this[T value] =>
Predicate(value) ? new Enumerable<T>(value) : Enumerable<T>.Empty;
public Enumerable<T> this[IEnumerable<T> values] =>
new Enumerable<T>(values.Where(i => Predicate(i)));
public static Set<T> operator &(Set<T> left, Set<T> right) =>
new Set<T>(i => left.Predicate(i) && right.Predicate(i));
public static Set<T> operator &(Set<T> left, IEnumerable<T> right) =>
left & new Set<T>(right);
public static Set<T> operator &(IEnumerable<T> left, Set<T> right) =>
new Set<T>(left) & right;
public static Set<T> operator |(Set<T> left, Set<T> right) =>
new Set<T>(i => left.Predicate(i) || right.Predicate(i));
public static Set<T> operator |(Set<T> left, IEnumerable<T> right) =>
left | new Set<T>(right);
public static Set<T> operator |(IEnumerable<T> left, Set<T> right) =>
new Set<T>(left) | right;
public static Set<T> operator -(Set<T> left, Set<T> right) =>
new Set<T>(i => left.Predicate(i) && !right.Predicate(i));
public static Set<T> operator -(Set<T> left, IEnumerable<T> right) =>
left - new Set<T>(right);
public static Set<T> operator -(IEnumerable<T> left, Set<T> right) =>
new Set<T>(left) - right;
public static Set<T> operator !(Set<T> set) =>
new Set<T>(i => !set.Predicate(i));
Predicate <T> Predicate { get; }
class Enumerable<T> : IEnumerable<T>
public static readonly Enumerable<T> Empty = new Enumerable<T>();
public static implicit operator bool(Enumerable<T> intersection) => intersection.Any();
public Enumerable(params T[] items)
public Enumerable(IEnumerable<T> items)
public IEnumerator<T> GetEnumerator() => Items.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
IEnumerable<T> Items { get; }