Commit 7f828d7c authored by Reinhard Munz's avatar Reinhard Munz

added source files

parent c409cf92
namespace Qbb.Core.Bookkeeper
{
using PINQ;
using Qbb.Core.Partition;
using System;
using System.Collections.Generic;
using System.Linq;
public class BKQueryable<R>
{
public IQueryable<R> Queryable { get; private set; }
public Bookkeeper<R> Bookkeeper { get; private set; }
public Partition Current { get; private set; }
public BKQueryable(IQueryable<R> queryable, Bookkeeper<R> bookkeeper, Partition current)
{
Queryable = queryable ?? throw new ArgumentNullException(nameof(queryable));
Bookkeeper = bookkeeper ?? throw new ArgumentNullException(nameof(bookkeeper));
Current = current ?? throw new ArgumentNullException(nameof(current));
}
public BKQueryable(IQueryable<R> queryable, Bookkeeper<R> bookkeeper, ContiguousPartition current) :
this(queryable, bookkeeper, new Partition(new HashSet<ContiguousPartition>() { current })) { }
public long MaxUsage()
{
return Bookkeeper.MaxUsage(Current).Epsilon;
}
public void WriteDetailedBudgetUseToFile(string fileName)
{
if (fileName == null) throw new ArgumentNullException(nameof(fileName));
Bookkeeper.WriteDetailedBudgetUseToFile(fileName);
}
public int BudgetIndex() => Bookkeeper.BudgetIndex();
public Boolean CoversDataspace()
{
return Current.Parts.Count > 0;
}
public PINQueryable<R> AsPinq(long epsilon)
{
return Bookkeeper.CreatePinQueryable(Queryable, Current, epsilon);
}
public void CleanHistory() => Bookkeeper.CleanHistory();
public BKQueryable<R> Materialize()
{
return new BKQueryable<R>(Bookkeeper.Materialize(Queryable, Current), Bookkeeper, Current);
}
public BKQueryable<R> Apply(Func<Partition, Partition> function)
{
if (function == null) throw new ArgumentNullException(nameof(function));
return new BKQueryable<R>(Queryable, Bookkeeper, function(Current));
}
public BKQueryable<R> Except(BKQueryable<R> other)
{
if (other == null) throw new ArgumentNullException(nameof(other));
if (other.Bookkeeper != Bookkeeper) throw new ArgumentException("bookkeepers do not match");
return new BKQueryable<R>(Queryable, Bookkeeper, Current.RemainderWithout(other.Current));
}
public BKQueryable<R> Intersect(BKQueryable<R> other)
{
if (other == null) throw new ArgumentNullException(nameof(other));
if (other.Bookkeeper != Bookkeeper) throw new ArgumentException("bookkeepers do not match");
return new BKQueryable<R>(Queryable, Bookkeeper, Current.IntersectionWith(other.Current));
}
public BKQueryable<R> Union(BKQueryable<R> other)
{
if (other == null) throw new ArgumentNullException(nameof(other));
if (other.Bookkeeper != Bookkeeper) throw new ArgumentException("bookkeepers do not match");
return new BKQueryable<R>(Queryable, Bookkeeper, Current.UnionWith(other.Current));
}
public override string ToString()
{
return "BKQ:\n" + Bookkeeper + "\n" + Current;
}
}
}
\ No newline at end of file
namespace Qbb.Core.Bookkeeper
{
using PINQ;
using Qbb.Core.BoundedData;
using Qbb.Core.History;
using Qbb.Core.Partition;
using System;
using System.Collections.Generic;
using System.Linq;
public class Bookkeeper<R>
{
private BookkeeperState State;
private BoundedDataAccess<R> BoundedDataAccess;
public Bookkeeper(BookkeeperState state, BoundedDataAccess<R> boundedDataAccess)
{
#if DEBUG
if (state == null) throw new ArgumentNullException(nameof(state));
if (boundedDataAccess == null) throw new ArgumentNullException(nameof(boundedDataAccess));
#endif
State = state;
BoundedDataAccess = boundedDataAccess;
}
public Bookkeeper(Partition focus, BoundedDataAccess<R> boundedDataAccess, IHistory history)
{
#if DEBUG
if (focus == null) throw new ArgumentNullException(nameof(focus));
if (boundedDataAccess == null) throw new ArgumentNullException(nameof(boundedDataAccess));
if (history == null) throw new ArgumentNullException(nameof(history));
#endif
State = new BookkeeperState
{
Forbidden = new Partition(new HashSet<ContiguousPartition>()),
Focus = focus ?? throw new ArgumentNullException(nameof(focus)),
History = history ?? throw new ArgumentNullException(nameof(history)),
NumberPartitionsAfterCleanCount = 0
};
BoundedDataAccess = boundedDataAccess ?? throw new ArgumentNullException(nameof(boundedDataAccess));
}
public Bookkeeper(ContiguousPartition focus, BoundedDataAccess<R> boundedDataAccess, IHistory history) :
this(new Partition(new HashSet<ContiguousPartition>() { focus }), boundedDataAccess, history) { }
private Partition GetFocus()
{
return State.Focus;
}
private void SetFocus(Partition focus)
{
if (focus == null) throw new ArgumentNullException(nameof(focus));
#if DEBUG
if (focus.Parts.Any() && BoundedDataAccess.Record.Fields.Length != focus.Parts.First().Intervals.Length)
throw new ArgumentException("intervals do not match");
#endif
if (focus.Intersects(State.Forbidden)) throw new ArgumentException("focus cannot be expanded into previous focus space");
var boundary = new Partition(new HashSet<ContiguousPartition>() { BoundedDataAccess.Record.Boundary });
if (!boundary.Encloses(focus)) throw new ArgumentException("focus cannot be expanded into space that does not exist in the database");
if (State.Focus != null && !focus.Encloses(State.Focus)) State.Forbidden = State.Forbidden.UnionWith(focus.Intersects(State.Focus) ? State.Focus.RemainderWithout(focus) : State.Focus);
State.Focus = focus;
}
public int NumberPartitions()
{
return State.History.Count;
}
public int NumberPartitionsAfterClean()
{
return State.NumberPartitionsAfterCleanCount;
}
public int BudgetIndex() => BoundedDataAccess.Record.GetBudgetIndex();
private bool BudgetGreaterEquals(Partition partition, long minBudget)
{
#if DEBUG
if (partition == null) throw new ArgumentNullException(nameof(partition));
#endif
if (minBudget < 0) throw new ArgumentException("minBudget is negative");
if (minBudget == 0) return true;
var b = minBudget - 1;
var newIntervals = new Interval[BoundedDataAccess.Record.Fields.Length];
for (var i = 0; i < newIntervals.Length; i++) newIntervals[i] = i != BoundedDataAccess.Record.GetBudgetIndex() ?
new Interval(long.MinValue,long.MaxValue) : new Interval(0L, b);
return !partition.Intersects(new ContiguousPartition(newIntervals));
}
public PINQueryable<R> CreatePinQueryable(IQueryable<R> queryable, Partition partition, long epsilon)
{
if (queryable == null) throw new ArgumentNullException(nameof(queryable));
if (partition == null) throw new ArgumentNullException(nameof(partition));
if (partition.Count() == 0) throw new ArgumentException("partition contains no parts");
if (!GetFocus().Encloses(partition)) throw new ArgumentException("partition is not fully in focus");
if (!BudgetGreaterEquals(partition, epsilon)) throw new ArgumentException("not enough budget");
var intersectionToBudget = new Dictionary<Partition, long>();
var overlapInformation = State.History.Overlapping(partition);
for (int i = 0; i < overlapInformation.Overlappings.Count; i++)
{
var intersection = overlapInformation.Intersections[i];
var budget = overlapInformation.Overlappings[i].Epsilon + epsilon;
if (!BudgetGreaterEquals(intersection, budget)) throw new ArgumentException("not enough budget");
if (intersectionToBudget.ContainsKey(intersection) && intersectionToBudget[intersection] > budget) budget = intersectionToBudget[intersection];
intersectionToBudget[intersection] = budget;
}
State.History.Add(new HistoryEntry(partition, epsilon));
foreach (var kv in intersectionToBudget) State.History.Add(new HistoryEntry(kv.Key, kv.Value));
return new PINQueryable<R>(BoundedDataAccess.BoundQueryable(queryable, partition),
new PINQAgentBudget(BoundedDataAccess.Record.Fields[BoundedDataAccess.Record.GetBudgetIndex()].FieldType.Convert(epsilon, true)));
}
public EpsilonInformation MaxUsage(Partition partition)
{
if (partition == null) throw new ArgumentNullException(nameof(partition));
var o = State.History.Overlapping(partition);
return !o.Overlappings.Any() ? new EpsilonInformation(null, 0L) : new EpsilonInformation(o.MaxPartition, o.MaxEpsilon);
}
public void CleanHistory()
{
State.History.Clean();
State.NumberPartitionsAfterCleanCount = State.History.Count;
}
public IQueryable<R> Materialize(IQueryable<R> queryable, Partition partition)
{
if (queryable == null) throw new ArgumentNullException(nameof(queryable));
if (partition == null) throw new ArgumentNullException(nameof(partition));
if (partition.Count() == 0) throw new ArgumentException("partition contains no parts");
return BoundedDataAccess.BoundQueryable(queryable, partition).ToArray().AsQueryable();
}
public void WriteDetailedBudgetUseToFile(string fileName)
{
#if DEBUG
if (fileName == null) throw new ArgumentNullException(nameof(fileName));
#endif
State.History.WriteDetailedBudgetUseToFile(fileName, BoundedDataAccess, BoundedDataAccess.Record.Fields[BoundedDataAccess.Record.GetBudgetIndex()].FieldType);
}
public void ExtendFocus(Partition extension)
{
if (extension == null) throw new ArgumentNullException(nameof(extension));
SetFocus(GetFocus().UnionWith(extension));
GetFocus().Clean();
}
public override string ToString()
{
var ret = "[BK: ...\n";
ret += State.History.ToString();
ret += "]\n";
return ret;
}
}
}
\ No newline at end of file
namespace Qbb.Core.Bookkeeper
{
using Qbb.Core.History;
using Qbb.Core.Partition;
public class BookkeeperState
{
public Partition Focus;
public Partition Forbidden;
public IHistory History;
public int NumberPartitionsAfterCleanCount;
}
}
\ No newline at end of file
namespace Qbb.Core.Bookkeeper
{
using Qbb.Core.Partition;
using System;
public class EpsilonInformation
{
public Partition Partition { get; private set; }
public long Epsilon { get; private set; }
public EpsilonInformation(Partition partition, long epsiplon)
{
// WARNING: Partition can be null if there is no partition
Partition = partition;
Epsilon = epsiplon;
}
public override bool Equals(object obj)
{
if (!(obj is EpsilonInformation o)) return false;
if (!Partition.Equals(o.Partition)) return false;
return Epsilon == o.Epsilon;
}
public override int GetHashCode()
{
throw new NotSupportedException("cannot get perfect hash of partition");
}
}
}
\ No newline at end of file
namespace Qbb.Core.BoundedData
{
using Qbb.Core.History;
using Qbb.Core.Partition;
using Qbb.Core.Specification;
using System;
using System.Collections.Generic;
using System.Linq;
public class BoundedDataAccess<R> : ICounting
{
public Record<R> Record { get; private set; }
public IQueryableFactory<R> QueryableFactory { get; private set; }
private ExpressionProvider<R> ExpressionProvider { get; set; }
public BoundedDataAccess(Record<R> record, IQueryableFactory<R> queryableFactory, ExpressionProvider<R> expressionProvider)
{
#if DEBUG
if (record == null) throw new ArgumentNullException(nameof(record));
if (queryableFactory == null) throw new ArgumentNullException(nameof(queryableFactory));
if (expressionProvider == null) throw new ArgumentNullException(nameof(expressionProvider));
#endif
Record = record;
QueryableFactory = queryableFactory;
ExpressionProvider = expressionProvider;
}
public IQueryable<R> GetQueryable()
{
return QueryableFactory.Create();
}
public IQueryable<R> BoundQueryable(IQueryable<R> queryable, Partition partition)
{
#if DEBUG
if (queryable == null) throw new ArgumentNullException(nameof(queryable));
if (partition == null) throw new ArgumentNullException(nameof(partition));
if (partition.Parts.Count == 0) throw new ArgumentException("partition contains no parts");
#endif
return queryable.Where(ExpressionProvider.EnclosedIn(partition, Record, null));
}
public override string ToString()
{
return "[BDA: ...]";
}
public int CountWithin(ContiguousPartition contiguousPartition)
{
#if DEBUG
if (contiguousPartition == null) throw new ArgumentNullException(nameof(contiguousPartition));
#endif
return BoundQueryable(GetQueryable(), new Partition(new HashSet<ContiguousPartition>() { contiguousPartition })).Count();
}
public int CountAll()
{
return CountWithin(Record.Boundary);
}
}
}
\ No newline at end of file
namespace Qbb.Core.BoundedData
{
using Qbb.Core.Partition;
using Qbb.Core.Specification;
using Qbb.Core.Utils;
using System;
using System.Linq;
using System.Linq.Expressions;
public class ExpressionProvider<R>
{
public Expression<Func<R, bool>> EnclosedIn(Partition partition, Record<R> record, ParameterExpression parameter)
{
#if DEBUG
if (partition == null) throw new ArgumentNullException(nameof(partition));
if (record == null) throw new ArgumentNullException(nameof(record));
#endif
ParameterExpression currentParameter = parameter;
if (currentParameter == null) currentParameter = Expression.Parameter(typeof(R));
return partition.Parts.Aggregate<ContiguousPartition, Expression<Func<R, bool>>>(null, (e, p) =>
{
if (e == null) return EnclosedIn(p, currentParameter, record);
else return Expression.Lambda<Func<R, bool>>(Expression.OrElse(e.Body, EnclosedIn(p, currentParameter, record).Body), currentParameter);
});
}
public Expression<Func<R, bool>> EnclosedIn(ContiguousPartition contiguousPartition, ParameterExpression parameter, Record<R> record)
{
#if DEBUG
if (contiguousPartition == null) throw new ArgumentNullException(nameof(contiguousPartition));
if (record == null) throw new ArgumentNullException(nameof(record));
if (contiguousPartition.Intervals.Length != record.Fields.Length) throw new ArgumentException("intervals do not match");
#endif
Expression expression = null;
ParameterExpression currentParameter = parameter;
if (currentParameter == null) currentParameter = Expression.Parameter(typeof(R));
for (var i = 0; i < contiguousPartition.Intervals.Length; i++)
{
var field = record.Fields[i];
var range = contiguousPartition.Intervals[i];
var accessVisitor = new ReplaceExpressionVisitor(field.Access.Parameters[0], currentParameter);
var accessBody = accessVisitor.Visit(field.Access.Body);
if (field.FieldType.Bounds.Low < range.Low)
{
var exp = Expression.GreaterThanOrEqual(accessBody, Expression.Constant(field.FieldType.Convert(range.Low, false)));
if (expression == null) expression = exp;
else expression = Expression.AndAlso(expression, exp);
}
if (range.High < field.FieldType.Bounds.High)
{
var exp = Expression.LessThanOrEqual(accessBody, Expression.Constant(field.FieldType.Convert(range.High, true)));
if (expression == null) expression = exp;
else expression = Expression.AndAlso(expression, exp);
}
}
if (expression == null) expression = Expression.Constant(true);
return Expression.Lambda<Func<R, bool>>(expression, currentParameter);
}
}
}
\ No newline at end of file
namespace Qbb.Core.BoundedData
{
using System.Linq;
public interface IQueryableFactory<R>
{
IQueryable<R> Create();
}
}
\ No newline at end of file
namespace Qbb.Core.History
{
using Qbb.Core.Partition;
using System;
public class HistoryEntry
{
public Partition Partition { get; private set; }
public long Epsilon { get; private set; }
public HistoryEntry(Partition partition, long epsilon)
{
#if DEBUG
if (partition == null) throw new ArgumentNullException(nameof(partition));
#endif
Partition = partition;
Epsilon = epsilon;
}
public override bool Equals(object obj)
{
if (!(obj is HistoryEntry o)) return false;
return Partition.Equals(o.Partition) && Epsilon == o.Epsilon;
}
public override int GetHashCode()
{
throw new NotSupportedException("cannot get perfect hash of partition");
}
}
}
\ No newline at end of file
namespace Qbb.Core.History
{
using Qbb.Core.Partition;
public interface ICounting
{
int CountWithin(ContiguousPartition contiguousPartition);
int CountAll();
}
}
\ No newline at end of file
namespace Qbb.Core.History
{
using Qbb.Core.Partition;
using Qbb.Core.Specification;
public interface IHistory
{
int Count { get; }
void Add(HistoryEntry historyEntry);
void Clean();
OverlapInformation Overlapping(Partition partition);
void WriteDetailedBudgetUseToFile(string fileName, ICounting counting, IFieldType budgetType);
string ToString();
}
}
\ No newline at end of file
namespace Qbb.Core.History
{
using Qbb.Core.Partition;
using Qbb.Core.Specification;
using System;
using System.Collections.Generic;
using System.Linq;
public class InMemoryHistory : IHistory
{
public int BudgetIndex { get; private set; }
public int Count { get; private set; }
private SortedDictionary<long, Partition> History;
private SortedDictionary<long, Partition> WasExpanded;
private SortedDictionary<long, Partition> NeedsCleaning;
public InMemoryHistory(int budgetIndex)
{
BudgetIndex = budgetIndex;
Count = 0;
History = new SortedDictionary<long, Partition>();
WasExpanded = new SortedDictionary<long, Partition>();
NeedsCleaning = new SortedDictionary<long, Partition>();
}
public void Add(HistoryEntry historyEntry)
{
#if DEBUG
if (historyEntry == null) throw new ArgumentNullException(nameof(historyEntry));
#endif
var contains = History.TryGetValue(historyEntry.Epsilon, out Partition partition);
var previousPartitionSize = contains ? partition.Count() : 0;
var newPartition = contains ? partition.UnionWith(historyEntry.Partition)