예제 #1
0
    def test_parameters(self):
        i1, i2, i3, i4 = P.closed(0, 1), P.openclosed(0, 1), P.closedopen(0, 1), P.open(0, 1)
        params = {
            'disj': ' or ',
            'sep': '-',
            'left_open': '<!',
            'left_closed': '<',
            'right_open': '!>',
            'right_closed': '>',
            'conv': lambda s: '"{}"'.format(s),
            'pinf': '+oo',
            'ninf': '-oo',
        }

        assert P.to_string(i1, **params) == '<"0"-"1">'
        assert P.to_string(i2, **params) == '<!"0"-"1">'
        assert P.to_string(i3, **params) == '<"0"-"1"!>'
        assert P.to_string(i4, **params) == '<!"0"-"1"!>'

        assert P.to_string(P.empty(), **params) == '<!!>'
        assert P.to_string(P.singleton(1), **params) == '<"1">'

        assert P.to_string(P.openclosed(-P.inf, 1), **params) == '<!-oo-"1">'
        assert P.to_string(P.closedopen(1, P.inf), **params) == '<"1"-+oo!>'

        assert P.to_string(P.closed(0, 1) | P.closed(2, 3), **params) == '<"0"-"1"> or <"2"-"3">'
예제 #2
0
    def test_with_intervals(self):
        d = P.IntervalDict([(P.closed(0, 2), 0)])
        assert d[P.open(-P.inf, P.inf)].as_dict() == {P.closed(0, 2): 0}
        assert d[P.closed(0, 2)].as_dict() == {P.closed(0, 2): 0}
        assert d[P.closed(-1, 0)].as_dict() == {P.singleton(0): 0}
        assert d[P.closed(-2, -1)].as_dict() == {}
        assert d.get(P.closed(0, 2)).as_dict() == {P.closed(0, 2): 0}
        assert d.get(P.closed(-2, -1)).as_dict() == {P.closed(-2, -1): None}
        assert d.get(P.closed(-1, 0)).as_dict() == {
            P.closedopen(-1, 0): None,
            P.singleton(0): 0
        }

        d[P.closed(1, 3)] = 1
        assert d.as_dict() == {P.closedopen(0, 1): 0, P.closed(1, 3): 1}
        assert len(d) == 2
        assert d[0] == 0
        assert d.get(0, -1) == 0
        assert d[1] == 1
        assert d.get(1, -1) == 1
        assert d[3] == 1
        assert d.get(3, -1) == 1
        with pytest.raises(KeyError):
            d[4]
        assert d.get(4, -1) == -1
예제 #3
0
    def splice(cls, S1, I1, S2, I2):
        intervals = []
        ancestors = []

        fwd = lambda I: P.closedopen(I.lower - I1.lower + I2.lower, I.upper -
                                     I1.upper + I2.upper)
        rev = lambda I: P.closedopen(I.lower - I2.lower + I1.lower, I.upper -
                                     I2.upper + I1.upper)

        for I, a in S1.items():
            if I.overlaps(I1):
                d, o = I - I1, I & I1
                if not d.empty:
                    intervals.append(d)
                    ancestors.append(a.transform(I, lambda I: I - I1))

                # import ipdb; ipdb.set_trace()
                for II, aa in S2[P.Interval(*[fwd(atom)
                                              for atom in o])].items():
                    intervals.append(P.Interval(*[rev(atom) for atom in II]))
                    ancestors.append(aa)
            else:
                intervals.append(I)
                ancestors.append(a)

        return cls(intervals, ancestors)
예제 #4
0
 def test_with_smaller(self):
     assert P.closed(0, 4) - P.closed(2, 3) == P.closedopen(
         0, 2) | P.openclosed(3, 4)
     assert P.closed(1, 4) - P.closed(1, 3) == P.openclosed(3, 4)
     assert P.closed(1, 4) - P.closed(2, 4) == P.closedopen(1, 2)
     assert P.closed(0, 4) - P.open(1, 2) == P.closed(0, 1) | P.closed(2, 4)
     assert P.closed(0, 2) - P.open(0, 2) == P.singleton(0) | P.singleton(2)
예제 #5
0
    def test_parameters(self):
        i1, i2, i3, i4 = '<"0"-"1">', '<!"0"-"1">', '<"0"-"1"!>', '<!"0"-"1"!>'
        params = {
            'conv': lambda s: int(s[1:-1]),
            'disj': ' or ',
            'sep': '-',
            'left_open': '<!',
            'left_closed': '<',
            'right_open': '!>',
            'right_closed': '>',
            'pinf': r'\+oo',
            'ninf': '-oo',
        }

        assert P.from_string(i1, **params) == P.closed(0, 1)
        assert P.from_string(i2, **params) == P.openclosed(0, 1)
        assert P.from_string(i3, **params) == P.closedopen(0, 1)
        assert P.from_string(i4, **params) == P.open(0, 1)

        assert P.from_string('<!!>', **params) == P.empty()
        assert P.from_string('<"1">', **params) == P.singleton(1)

        assert P.from_string('<!-oo-"1">', **params) == P.openclosed(-P.inf, 1)
        assert P.from_string('<"1"-+oo!>', **params) == P.closedopen(1, P.inf)

        assert P.from_string('<"0"-"1"> or <"2"-"3">', **params) == P.closed(0, 1) | P.closed(2, 3)
예제 #6
0
    def test_combine_nonempty(self):
        add = lambda x, y: x + y
        d1 = P.IntervalDict([(P.closed(1, 3) | P.closed(5, 7), 1)])
        d2 = P.IntervalDict([(P.closed(2, 4) | P.closed(6, 8), 2)])
        assert d1.combine(d2, add) == d2.combine(d1, add)
        assert d1.combine(d2, add) == P.IntervalDict([
            (P.closedopen(1, 2) | P.closedopen(5, 6), 1),
            (P.closed(2, 3) | P.closed(6, 7), 3),
            (P.openclosed(3, 4) | P.openclosed(7, 8), 2),
        ])

        d1 = P.IntervalDict({
            P.closed(0, 1): 2,
            P.closed(3, 4): 2
        })
        d2 = P.IntervalDict({
            P.closed(1, 3): 3,
            P.closed(4, 5): 1
        })
        assert d1.combine(d2, add) == d2.combine(d1, add)
        assert d1.combine(d2, add) == P.IntervalDict({
            P.closedopen(0, 1): 2,
            P.singleton(1): 5,
            P.open(1, 3): 3,
            P.singleton(3): 5,
            P.open(3, 4): 2,
            P.singleton(4): 3,
            P.openclosed(4, 5): 1,
        })
예제 #7
0
 def test_with_adjacent(self):
     assert P.closed(1, 2) | P.closed(2, 3) == P.closed(1, 3)
     assert P.closed(1, 2) | P.open(2, 3) == P.closedopen(1, 3)
     assert P.open(1, 2) | P.closed(2, 3) == P.openclosed(1, 3)
     assert P.open(1, 3) | P.open(2, 4) == P.open(1, 4)
     assert P.closedopen(1, 2) | P.closed(2, 3) == P.closed(1, 3)
     assert P.open(1, 2) | P.closed(2, 4) == P.openclosed(1, 4)
예제 #8
0
    def constraint(self, op, version=None):
        if version is None:
            version = op
            op = '*'

        major, minor, patch = version

        if op == '*':
            if major == '*':
                return I.closedopen(Version.FIRST, I.inf)
            elif minor == '*':
                return minor_interval(Version(major, 0, 0))
            elif patch == '*':
                return patch_interval(Version(major, minor, 0))
            else:
                # Equivalent to ^x.y.z
                op = '^'

        if op == '^':
            if minor is None:
                # ^1 := >=1.0.0 <2.0.0
                # ^0 := >=0.0.0 <1.0.0
                return minor_interval(Version(major, 0, 0))
            elif patch is None:
                # ^1.2 := >=1.2.0 <2.0.0
                # ^0.0 := >=0.0.0 <0.1.0
                if major == 0:
                    return patch_interval(Version(major, minor, 0))
                else:
                    return minor_interval(Version(major, minor, 0))
            else:
                # ^1.2.3 := >=1.2.3 <2.0.0
                # ^0.2.3 := >=0.2.3 <0.3.0
                # ^0.0.3 := >=0.0.3 <0.0.4
                if major == 0:
                    if minor == 0:
                        return I.closedopen(Version(0, 0, patch),
                                            Version(0, 0, patch + 1))
                    else:
                        return patch_interval(Version(0, minor, patch))
                else:
                    return minor_interval(Version(major, minor, patch))
        elif op == '~':
            if minor is None:
                # ~1 := >=1.0.0 <2.0.0
                return minor_interval(Version(major, 0, 0))
            elif patch is None:
                # ~1.2 := >=1.2.0 <1.3.0
                return patch_interval(Version(major, minor, 0))
            else:
                # ~1.2.3 := >=1.2.3 <1.3.0
                return patch_interval(Version(major, minor, patch))
        else:
            # =, >=, >, <=, <, !=
            minor = 0 if minor is None else minor
            patch = 0 if patch is None else patch
            return comparator_interval(op, Version(major, minor, patch))

        assert False, (op, version)
예제 #9
0
    def test_with_unions(self):
        assert P.closed(0, 1) | P.closed(2, 3) in P.closed(0, 4)
        assert P.closed(0, 1) | P.closed(2, 3) in P.closed(0, 1) | P.closed(2, 3)
        assert P.closed(0, 1) | P.closed(2, 3) in P.closed(0, 0) | P.closed(0, 1) | P.closed(2, 3)

        assert P.closed(0, 1) | P.closed(2, 3) not in P.closed(0, 2)
        assert P.closed(0, 1) | P.closed(2, 3) not in P.closed(0, 1) | P.closedopen(2, 3)
        assert P.closed(0, 1) | P.closed(2, 3) not in P.closed(0, 1) | P.closedopen(2, 3) | P.openclosed(3, 4)
예제 #10
0
 def complete(self, data_desc: fieldop.DataDesc):
     if not self.contiguous():
         return False
     if self.domain_.size == 0:
         return False
     if self.enclosure() == KInterval(P.closedopen(0, data_desc.totlonlen), P.closedopen(0, data_desc.totlatlen)):
         return True
     return False
예제 #11
0
        def Put(A, anciv, C, curiv, l):
            if curiv[0] >= curiv[1]:
                curiv = (curiv[0], curiv[1] + l)
            vals = [(C, closedopen(curiv[0], curiv[1]))]

            for val in vals:
                if anciv[0] >= anciv[1]:
                    anciv = (anciv[0], anciv[1] + self.L)
                blks[int(A)].put(closedopen(anciv[0], anciv[1] + 1), val)
예제 #12
0
def is_overlapping(event_a: CalendarEvent, event_b: CalendarEvent) -> bool:
    """
    Tell if two events (with datetime) are overlapping.
    """

    interval_a = P.closedopen(event_a.start_time, event_a.end_time)
    interval_b = P.closedopen(event_b.start_time, event_b.end_time)

    return not (interval_a & interval_b).empty
예제 #13
0
    def _reassemble(self, ctr):
        if 'deliver' not in ctr.actions:
            return
        if not (ctr.bundle.primary.bundle_flags & PrimaryBlock.Flag.IS_FRAGMENT):
            return

        final_ident = ctr.bundle_ident()[:3]
        frag_offset = ctr.bundle.primary.fragment_offset
        total_length = ctr.bundle.primary.total_app_data_len

        reassm = self._reassembly.get(final_ident, None)
        if reassm is None:
            reassm = Reassembly(
                ident=final_ident,
                total_length=total_length,
                total_valid=portion.closedopen(0, total_length),
                valid=portion.empty(),
                data=bytearray(total_length)
            )
            self._reassembly[final_ident] = reassm
        else:
            if reassm.total_length != total_length:
                LOGGER.warning('Mismatch in fragment-bundle total application data length')

        if frag_offset == 0:
            reassm.first_frag = ctr.bundle

        # Inject the new fragment
        payload_data = ctr.block_num(1).getfieldval('btsd')
        end_ix = frag_offset + len(payload_data)
        reassm.data[frag_offset:end_ix] = payload_data
        reassm.valid |= portion.closedopen(frag_offset, end_ix)

        if reassm.valid == reassm.total_valid:
            del self._reassembly[final_ident]
            LOGGER.info('Finished reassembly of %s size %d', final_ident, reassm.total_length)

            # Synthesize original bundle
            rctr = BundleContainer()
            rctr.bundle.primary = reassm.first_frag.primary.copy()
            rctr.bundle.primary.bundle_flags &= ~PrimaryBlock.Flag.IS_FRAGMENT
            rctr.bundle.primary.crc_type = AbstractBlock.CrcType.NONE
            rctr.bundle.primary.crc_value = None

            LOGGER.debug('Copying %d first-fragment blocks', len(reassm.first_frag.blocks))
            for blk in reassm.first_frag.blocks:
                rctr.bundle.blocks.append(blk.copy())
            rctr.reload()
            pyld_blk = rctr.block_num(Bundle.BLOCK_NUM_PAYLOAD)
            pyld_blk.setfieldval('btsd', reassm.data)
            pyld_blk.crc_type = AbstractBlock.CrcType.NONE
            pyld_blk.crc_value = None

            glib.idle_add(self._agent.recv_bundle, rctr)

        ctr.actions.clear()
        return True
예제 #14
0
 def test_replace_with_union(self):
     i = P.closed(0, 1) | P.open(2, 3)
     assert i.replace() == i
     assert i.replace(P.OPEN, -1, 4, P.OPEN) == P.openclosed(-1, 1) | P.open(2, 4)
     assert i.replace(lower=2) == P.closedopen(2, 3)
     assert i.replace(upper=1) == P.closedopen(0, 1)
     assert i.replace(lower=5) == P.empty()
     assert i.replace(upper=-5) == P.empty()
     assert i.replace(left=lambda v: ~v, lower=lambda v: v - 1, upper=lambda v: v + 1, right=lambda v: ~v) == P.openclosed(-1, 1) | P.openclosed(2, 4)
예제 #15
0
def transform(interval, domain, func):
    fwd = lambda I: P.closedopen(I.lower - domain.lower + interval.lower, I.
                                 upper - domain.upper + interval.upper)
    rev = lambda I: P.closedopen(I.lower - interval.lower + domain.lower, I.
                                 upper - interval.upper + domain.upper)

    return P.Interval(*[
        fwd(new_atom) for atom in interval for new_atom in func(rev(atom))
        if not new_atom.empty
    ])
예제 #16
0
 def test_overlaps_with_edge_cases(self):
     assert not P.closed(0, 1).overlaps(P.open(1, 2))
     assert not P.closed(0, 1).overlaps(P.openclosed(1, 2))
     assert not P.closedopen(0, 1).overlaps(P.closed(1, 2))
     assert not P.closedopen(0, 1).overlaps(P.closedopen(1, 2))
     assert not P.closedopen(0, 1).overlaps(P.openclosed(1, 2))
     assert not P.closedopen(0, 1).overlaps(P.open(1, 2))
     assert not P.open(0, 1).overlaps(P.open(1, 2))
     assert P.open(0, 2).overlaps(P.open(0, 1))
     assert P.open(0, 1).overlaps(P.open(0, 2))
예제 #17
0
    def test_non_adjacent(self):
        assert not P.closedopen(0, 1).adjacent(P.closedopen(3, 4))
        assert not P.closed(0, 1).adjacent(P.open(3, 4))
        assert not P.closed(0, 1).adjacent(P.closed(3, 4))
        assert not P.open(0, 1).adjacent(P.open(3, 4))

        assert not P.closedopen(3, 4).adjacent(P.closedopen(0, 1))
        assert not P.open(3, 4).adjacent(P.closed(0, 1))
        assert not P.closed(3, 4).adjacent(P.closed(0, 1))
        assert not P.open(3, 4).adjacent(P.open(0, 1))
예제 #18
0
    def test_setdefault_with_intervals(self):
        d = P.IntervalDict([(P.closed(0, 2), 0)])
        t = d.setdefault(P.closed(-2, -1), -1)
        assert t.items() == [(P.closed(-2, -1), -1)]
        assert d.items() == [(P.closed(-2, -1), -1), (P.closed(0, 2), 0)]

        d = P.IntervalDict([(P.closed(0, 2), 0)])
        t = d.setdefault(P.closed(-1, 1), 2)
        assert t.items() == [(P.closedopen(-1, 0), 2), (P.closed(0, 1), 0)]
        assert d.items() == [(P.closedopen(-1, 0), 2), (P.closed(0, 2), 0)]
예제 #19
0
    def test_overlapping(self):
        assert not P.openclosed(0, 2).adjacent(P.closedopen(2, 3))
        assert not P.closed(0, 2).adjacent(P.closedopen(2, 3))
        assert not P.closed(0, 2).adjacent(P.closed(2, 3))
        assert not P.open(0, 2).adjacent(P.open(2, 3))

        assert not P.closedopen(2, 3).adjacent(P.openclosed(0, 2))
        assert not P.closedopen(2, 3).adjacent(P.closed(0, 2))
        assert not P.closed(2, 3).adjacent(P.closed(0, 2))
        assert not P.open(2, 3).adjacent(P.open(0, 2))
예제 #20
0
    def test_setdefault_with_intervals(self):
        d = P.IntervalDict([(P.closed(0, 2), 0)])
        t = d.setdefault(P.closed(-2, -1), -1)
        assert t.as_dict() == {P.closed(-2, -1): -1}
        assert d.as_dict() == {P.closed(-2, -1): -1, P.closed(0, 2): 0}

        d = P.IntervalDict([(P.closed(0, 2), 0)])
        t = d.setdefault(P.closed(-1, 1), 2)
        assert t.as_dict() == {P.closedopen(-1, 0): 2, P.closed(0, 1): 0}
        assert d.as_dict() == {P.closedopen(-1, 0): 2, P.closed(0, 2): 0}
예제 #21
0
def pd_join_interval(dfs, v_lut):
    hl = dict()
    vl = []
    for i in range(2):
        if not v_lut[i]['from']:
            pd_create_from_to(dfs[i], v_lut[i]['hid'], v_lut[i]['to'], True)
            v_lut[i]['from'] = 'from'

        dfs[i].set_index(v_lut[i]['hid'], False, False, True)
        dfs[i].set_index(pd.RangeIndex(0, len(dfs[i])), False, True, True)

        # first step: construct a comprehensive list of intervals
        # where any of the inputs touches
        for row, df in dfs[i].iterrows():
            hid = df[v_lut[i]['hid']]
            if hid not in hl:
                hl[hid] = set()
            d = portion.closedopen(df[v_lut[i]['from']], df[v_lut[i]['to']])
            if not d.empty:
                hl[hid].add(d)

        # add variables one by one since we have to preserve order anyway
        for c in dfs[i].columns:
            if c not in vl:
                vl.append(c)
    t = time.time()
    odf = pd.DataFrame(columns=vl)
    for hid, dl in hl.items():
        print("#", hid)
        c0 = min(dl).lower
        c1 = None
        while True:
            r = pd.Series(None, vl, 'object', (hid, c0))
            cs = portion.singleton(c0)
            for i in range(len(dfs)):
                if hid in dfs[i].index:
                    for row_i, row_d in dfs[i].loc[hid].iterrows():
                        d = portion.closedopen(row_d[v_lut[i]['from']],
                                               row_d[v_lut[i]['to']])
                        if d.overlaps(cs):
                            r.update(row_d)
                            if c1 is None or c1 > d.upper:
                                c1 = d.upper
            if c1 is None:
                break
            r['from'] = c0
            r['to'] = c1
            c0 = c1
            c1 = None
            odf = odf.append(r)
    print("pd_join_interval profile time", time.time() - t)
    return odf
예제 #22
0
    def test_iterators(self):
        d = P.IntervalDict([(P.closedopen(0, 1), 0), (P.closedopen(1, 3), 1),
                            (P.singleton(3), 2)])

        assert d.keys() == [
            P.closedopen(0, 1),
            P.closedopen(1, 3),
            P.singleton(3)
        ]
        assert d.domain() == P.closed(0, 3)
        assert d.values() == [0, 1, 2]
        assert d.items() == list(zip(d.keys(), d.values()))
        assert list(d) == d.keys()
예제 #23
0
def horizontal_transfer(S1, S2):
    D1, D2 = S1.domain, S2.domain

    lb1 = rng.randint(D1.lower, high=D1.upper)
    ub1 = rng.randint(lb1, high=D1.upper)
    I1 = P.closedopen(lb1, ub1)

    delta = ub1 - lb1

    lb2 = rng.randint(D2.lower, (D2.upper - delta))
    ub2 = lb2 + delta
    I2 = P.closedopen(lb2, ub2)

    return I1, I2
예제 #24
0
def comparator_interval(op, version):
    if op == '=':
        return I.singleton(version)
    if op == '<':
        return I.closedopen(Version.FIRST, version)
    if op == '<=':
        return I.closed(Version.FIRST, version)
    if op == '>':
        return I.open(version, I.inf)
    if op == '>=':
        return I.closedopen(version, I.inf)
    if op == '!=':
        return I.closedopen(Version.FIRST, version) | I.openclosed(
            version, I.inf)
예제 #25
0
    def test_contained(self):
        assert not P.closed(0, 4).adjacent(P.closed(0, 2))
        assert not P.closed(0, 4).adjacent(P.closed(2, 4))
        assert not P.closed(0, 4).adjacent(P.open(0, 2))
        assert not P.closed(0, 4).adjacent(P.open(2, 4))

        assert not P.closed(0, 2).adjacent(P.closed(0, 4))
        assert not P.closed(2, 4).adjacent(P.closed(0, 4))
        assert not P.closed(0, 2).adjacent(P.open(0, 4))
        assert not P.closed(2, 4).adjacent(P.open(0, 4))

        assert not P.closed(0, 2).adjacent(P.closed(0, 2))
        assert not P.open(0, 2).adjacent(P.open(0, 2))
        assert not P.openclosed(0, 2).adjacent(P.openclosed(0, 2))
        assert not P.closedopen(0, 2).adjacent(P.closedopen(0, 2))
예제 #26
0
def set_rate_amount(apps, price, amount, start_date=None, end_date=None):
    """set_rate_amount method from current Price model."""
    RatePerDate = apps.get_model("core", "RatePerDate")
    new_period = create_interval(start_date, end_date)

    existing_periods = price.rates_per_date.all()
    d = P.IntervalDict()
    for period in existing_periods:
        interval = create_interval(period.start_date, period.end_date)
        d[interval] = period.rate

    # We generate all periods from scratch to avoid complicated
    # merging logic.
    existing_periods.delete()

    d[new_period] = amount
    for period in d.keys():
        for interval in list(period):
            # In case of composite intervals
            start = (
                interval.lower if isinstance(interval.lower, date) else None
            )
            end = interval.upper if isinstance(interval.upper, date) else None
            period_rate_dict = d[
                P.closedopen(interval.lower, interval.upper) or date.today()
            ]
            rate = period_rate_dict.values()[0]

            rpd = RatePerDate(
                start_date=start, end_date=end, rate=rate, main_rate=price
            )
            rpd.save()
예제 #27
0
 def test_open_intervals(self):
     assert P.from_data([(P.OPEN, float('-inf'), float('inf'), P.OPEN)
                         ]) == P.open(-P.inf, P.inf)
     assert P.from_data([(P.OPEN, float('-inf'), 0, P.CLOSED)
                         ]) == P.openclosed(-P.inf, 0)
     assert P.from_data([(P.CLOSED, 0, float('inf'), P.OPEN)
                         ]) == P.closedopen(0, P.inf)
예제 #28
0
    def test_identity(self):
        i1, i2, i3, i4 = P.closed(0, 1), P.openclosed(0, 1), P.closedopen(0, 1), P.open(0, 1)

        assert P.from_string(P.to_string(i1), int) == i1
        assert P.from_string(P.to_string(i2), int) == i2
        assert P.from_string(P.to_string(i3), int) == i3
        assert P.from_string(P.to_string(i4), int) == i4
예제 #29
0
def generate(seqs, tiles, L):
    blocks = defaultdict(P.IntervalDict)
    for ancestor, atom in tiles.keys():
        blocks[ancestor][atom] = random_sequence(area(atom))

    for seq in seqs:
        result = np.chararray(L)
        for I in seq.keys():
            for atom in I:
                block = seq[atom]
                assert len(block) == 1, "bad atom"

                tile = next(block.values())
                assert area(atom) == tile.area, "unequal lengths"

                anc = blocks[tile.a][tile.i]
                dom = domain(anc)
                fn = lambda I: P.closedopen(I.lower - dom.lower + atom.lower, I
                                            .upper - dom.upper + atom.upper)
                for i, s in anc.items():
                    ii = fn(i)
                    for l, c in enumerate(s):
                        result[l + ii.lower] = c

        yield result.tostring().decode('utf-8')
예제 #30
0
    def test_identity(self):
        i1, i2, i3, i4 = P.closed(0, 1), P.openclosed(0, 1), P.closedopen(0, 1), P.open(0, 1)

        assert P.from_data(P.to_data(i2)) == i2
        assert P.from_data(P.to_data(i3)) == i3
        assert P.from_data(P.to_data(i4)) == i4
        assert P.from_data(P.to_data(i1)) == i1