foscat 2025.7.3__py3-none-any.whl → 2025.8.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
foscat/UNET.py ADDED
@@ -0,0 +1,200 @@
1
+ import numpy as np
2
+
3
+ import foscat.scat_cov as sc
4
+ import foscat.HOrientedConvol as hs
5
+
6
+ class UNET:
7
+
8
+ def __init__(
9
+ self,
10
+ nparam=1,
11
+ KERNELSZ=3,
12
+ NORIENT=4,
13
+ chanlist=None,
14
+ in_nside=1,
15
+ n_chan_in=1,
16
+ n_chan_out=1,
17
+ cell_ids=None,
18
+ SEED=1234,
19
+ filename=None,
20
+ ):
21
+ self.f=sc.funct(KERNELSZ=KERNELSZ)
22
+
23
+ if chanlist is None:
24
+ nlayer=int(np.log2(in_nside))
25
+ chanlist=[4*2**k for k in range(nlayer)]
26
+ else:
27
+ nlayer=len(chanlist)
28
+ print('N_layer ',nlayer)
29
+
30
+ n=0
31
+ wconv={}
32
+ hconv={}
33
+ l_cell_ids={}
34
+ self.KERNELSZ=KERNELSZ
35
+ kernelsz=self.KERNELSZ
36
+
37
+ # create the CNN part
38
+ l_nside=in_nside
39
+ l_cell_ids[0]=cell_ids.copy()
40
+ l_data=self.f.backend.bk_cast(np.ones([1,1,l_cell_ids[0].shape[0]]))
41
+ l_chan=n_chan_in
42
+ print('Initial chan %d Npix=%d'%(l_chan,l_data.shape[2]))
43
+ for l in range(nlayer):
44
+ print('Layer %d Npix=%d'%(l,l_data.shape[2]))
45
+ # init double convol weights
46
+ wconv[2*l]=n
47
+ nw=l_chan*chanlist[l]*kernelsz*kernelsz
48
+ print('Layer %d conv [%d,%d]'%(l,l_chan,chanlist[l]))
49
+ n+=nw
50
+ wconv[2*l+1]=n
51
+ nw=chanlist[l]*chanlist[l]*kernelsz*kernelsz
52
+ print('Layer %d conv [%d,%d]'%(l,chanlist[l],chanlist[l]))
53
+ n+=nw
54
+
55
+ hconvol=hs.HOrientedConvol(l_nside,3,cell_ids=l_cell_ids[l])
56
+ hconvol.make_idx_weights()
57
+ hconv[l]=hconvol
58
+ l_data,n_cell_ids=self.f.ud_grade_2(l_data,cell_ids=l_cell_ids[l],nside=l_nside)
59
+ l_cell_ids[l+1]=self.f.backend.to_numpy(n_cell_ids)
60
+ l_nside//=2
61
+ # plus one to add the input downgrade data
62
+ l_chan=chanlist[l]+n_chan_in
63
+
64
+ self.n_cnn=n
65
+ self.l_cell_ids=l_cell_ids
66
+ self.wconv=wconv
67
+ self.hconv=hconv
68
+
69
+ # create the transpose CNN part
70
+ m_cell_ids={}
71
+ m_cell_ids[0]=l_cell_ids[nlayer]
72
+ t_wconv={}
73
+ t_hconv={}
74
+
75
+ for l in range(nlayer):
76
+ #upgrade data
77
+ l_chan+=n_chan_in
78
+ l_data=self.f.up_grade(l_data,l_nside*2,
79
+ cell_ids=l_cell_ids[nlayer-l],
80
+ o_cell_ids=l_cell_ids[nlayer-1-l],
81
+ nside=l_nside)
82
+ print('Transpose Layer %d Npix=%d'%(l,l_data.shape[2]))
83
+
84
+
85
+ m_cell_ids[l]=l_cell_ids[nlayer-1-l]
86
+ l_nside*=2
87
+
88
+ # init double convol weights
89
+ t_wconv[2*l]=n
90
+ nw=l_chan*l_chan*kernelsz*kernelsz
91
+ print('Transpose Layer %d conv [%d,%d]'%(l,l_chan,l_chan))
92
+ n+=nw
93
+ t_wconv[2*l+1]=n
94
+ out_chan=n_chan_out
95
+ if nlayer-1-l>0:
96
+ out_chan+=chanlist[nlayer-1-l]
97
+ print('Transpose Layer %d conv [%d,%d]'%(l,l_chan,out_chan))
98
+ nw=l_chan*out_chan*kernelsz*kernelsz
99
+ n+=nw
100
+
101
+ hconvol=hs.HOrientedConvol(l_nside,3,cell_ids=m_cell_ids[l])
102
+ hconvol.make_idx_weights()
103
+ t_hconv[l]=hconvol
104
+
105
+ # plus one to add the input downgrade data
106
+ l_chan=out_chan
107
+ print('Final chan %d Npix=%d'%(out_chan,l_data.shape[2]))
108
+ self.n_cnn=n
109
+ self.m_cell_ids=l_cell_ids
110
+ self.t_wconv=t_wconv
111
+ self.t_hconv=t_hconv
112
+ self.x=self.f.backend.bk_cast((np.random.rand(n)-0.5)/self.KERNELSZ)
113
+ self.nside=in_nside
114
+ self.n_chan_in=n_chan_in
115
+ self.n_chan_out=n_chan_out
116
+ self.chanlist=chanlist
117
+
118
+ def get_param(self):
119
+ return self.x
120
+
121
+ def set_param(self,x):
122
+ self.x=self.f.backend.bk_cast(x)
123
+
124
+ def eval(self,data):
125
+ # create the CNN part
126
+ l_nside=self.nside
127
+ l_chan=self.n_chan_in
128
+ l_data=data
129
+ m_data=data
130
+ nlayer=len(self.chanlist)
131
+ kernelsz=self.KERNELSZ
132
+ ud_data={}
133
+
134
+ for l in range(nlayer):
135
+ # init double convol weights
136
+ nw=l_chan*self.chanlist[l]*kernelsz*kernelsz
137
+ ww=self.x[self.wconv[2*l]:self.wconv[2*l]+nw]
138
+ ww=self.f.backend.bk_reshape(ww,[l_chan,
139
+ self.chanlist[l],
140
+ kernelsz*kernelsz])
141
+ l_data = self.hconv[l].Convol_torch(l_data,ww)
142
+
143
+ nw=self.chanlist[l]*self.chanlist[l]*kernelsz*kernelsz
144
+ ww=self.x[self.wconv[2*l+1]:self.wconv[2*l+1]+nw]
145
+ ww=self.f.backend.bk_reshape(ww,[self.chanlist[l],
146
+ self.chanlist[l],
147
+ kernelsz*kernelsz])
148
+
149
+ l_data = self.hconv[l].Convol_torch(l_data,ww)
150
+
151
+ l_data,_=self.f.ud_grade_2(l_data,
152
+ cell_ids=self.l_cell_ids[l],
153
+ nside=l_nside)
154
+
155
+ ud_data[l]=m_data
156
+
157
+ m_data,_=self.f.ud_grade_2(m_data,
158
+ cell_ids=self.l_cell_ids[l],
159
+ nside=l_nside)
160
+
161
+ l_data = self.f.backend.bk_concat([m_data,l_data],1)
162
+
163
+ l_nside//=2
164
+ # plus one to add the input downgrade data
165
+ l_chan=self.chanlist[l]+self.n_chan_in
166
+
167
+ for l in range(nlayer):
168
+ l_chan+=self.n_chan_in
169
+ l_data=self.f.up_grade(l_data,l_nside*2,
170
+ cell_ids=self.l_cell_ids[nlayer-l],
171
+ o_cell_ids=self.l_cell_ids[nlayer-1-l],
172
+ nside=l_nside)
173
+
174
+
175
+ l_data = self.f.backend.bk_concat([ud_data[nlayer-1-l],l_data],1)
176
+ l_nside*=2
177
+
178
+ # init double convol weights
179
+ out_chan=self.n_chan_out
180
+ if nlayer-1-l>0:
181
+ out_chan+=self.chanlist[nlayer-1-l]
182
+ nw=l_chan*l_chan*kernelsz*kernelsz
183
+ ww=self.x[self.t_wconv[2*l]:self.t_wconv[2*l]+nw]
184
+ ww=self.f.backend.bk_reshape(ww,[l_chan,
185
+ l_chan,
186
+ kernelsz*kernelsz])
187
+
188
+ c_data = self.t_hconv[l].Convol_torch(l_data,ww)
189
+
190
+ nw=l_chan*out_chan*kernelsz*kernelsz
191
+ ww=self.x[self.t_wconv[2*l+1]:self.t_wconv[2*l+1]+nw]
192
+ ww=self.f.backend.bk_reshape(ww,[l_chan,
193
+ out_chan,
194
+ kernelsz*kernelsz])
195
+ l_data = self.t_hconv[l].Convol_torch(c_data,ww)
196
+
197
+ # plus one to add the input downgrade data
198
+ l_chan=out_chan
199
+
200
+ return l_data
foscat/scat_cov.py CHANGED
@@ -33,7 +33,14 @@ testwarn = 0
33
33
 
34
34
 
35
35
  class scat_cov:
36
- def __init__(self, s0, s2, s3, s4, s1=None, s3p=None, backend=None, use_1D=False):
36
+ def __init__(self,
37
+ s0, s2, s3, s4,
38
+ s1=None,
39
+ s3p=None,
40
+ backend=None,
41
+ use_1D=False,
42
+ return_data=False
43
+ ):
37
44
  self.S0 = s0
38
45
  self.S2 = s2
39
46
  self.S3 = s3
@@ -44,12 +51,13 @@ class scat_cov:
44
51
  self.idx1 = None
45
52
  self.idx2 = None
46
53
  self.use_1D = use_1D
47
- self.numel = self.backend.bk_len(s0)+ \
48
- self.backend.bk_len(s1)+ \
49
- self.backend.bk_len(s2)+ \
50
- self.backend.bk_len(s3)+ \
51
- self.backend.bk_len(s4)+ \
52
- self.backend.bk_len(s3p)
54
+ if not return_data:
55
+ self.numel = self.backend.bk_len(s0)+ \
56
+ self.backend.bk_len(s1)+ \
57
+ self.backend.bk_len(s2)+ \
58
+ self.backend.bk_len(s3)+ \
59
+ self.backend.bk_len(s4)+ \
60
+ self.backend.bk_len(s3p)
53
61
 
54
62
  def numpy(self):
55
63
  if self.BACKEND == "numpy":
@@ -2825,6 +2833,7 @@ class funct(FOC.FoCUS):
2825
2833
  P1_dic = {}
2826
2834
  if cross:
2827
2835
  P2_dic = {}
2836
+
2828
2837
  elif (norm == "auto") and (self.P1_dic is not None):
2829
2838
  P1_dic = self.P1_dic
2830
2839
  if cross:
@@ -3647,7 +3656,9 @@ class funct(FOC.FoCUS):
3647
3656
  if calc_var:
3648
3657
  if not cross:
3649
3658
  return scat_cov(
3650
- s0, S2, S3, S4, s1=S1, backend=self.backend, use_1D=self.use_1D
3659
+ s0, S2, S3, S4, s1=S1, backend=self.backend,
3660
+ use_1D=self.use_1D,
3661
+ return_data=self.return_data
3651
3662
  ), scat_cov(
3652
3663
  vs0,
3653
3664
  VS2,
@@ -3656,6 +3667,7 @@ class funct(FOC.FoCUS):
3656
3667
  s1=VS1,
3657
3668
  backend=self.backend,
3658
3669
  use_1D=self.use_1D,
3670
+ return_data=self.return_data
3659
3671
  )
3660
3672
  else:
3661
3673
  return scat_cov(
@@ -3667,6 +3679,7 @@ class funct(FOC.FoCUS):
3667
3679
  s3p=S3P,
3668
3680
  backend=self.backend,
3669
3681
  use_1D=self.use_1D,
3682
+ return_data=self.return_data
3670
3683
  ), scat_cov(
3671
3684
  vs0,
3672
3685
  VS2,
@@ -3676,11 +3689,16 @@ class funct(FOC.FoCUS):
3676
3689
  s3p=VS3P,
3677
3690
  backend=self.backend,
3678
3691
  use_1D=self.use_1D,
3692
+ return_data=self.return_data
3679
3693
  )
3680
3694
  else:
3681
3695
  if not cross:
3682
3696
  return scat_cov(
3683
- s0, S2, S3, S4, s1=S1, backend=self.backend, use_1D=self.use_1D
3697
+ s0, S2, S3, S4,
3698
+ s1=S1,
3699
+ backend=self.backend,
3700
+ use_1D=self.use_1D,
3701
+ return_data=self.return_data
3684
3702
  )
3685
3703
  else:
3686
3704
  return scat_cov(
@@ -3692,6 +3710,7 @@ class funct(FOC.FoCUS):
3692
3710
  s3p=S3P,
3693
3711
  backend=self.backend,
3694
3712
  use_1D=self.use_1D,
3713
+ return_data=self.return_data
3695
3714
  )
3696
3715
 
3697
3716
  def clean_norm(self):
@@ -3769,8 +3788,12 @@ class funct(FOC.FoCUS):
3769
3788
  # cconv, sconv are [Nbatch, Norient3, Npix_j3]
3770
3789
  if self.use_1D:
3771
3790
  s3 = conv * self.backend.bk_conjugate(MconvPsi)
3791
+ elif self.use_2D:
3792
+ s3 = self.backend.bk_expand_dims(conv, -4)* self.backend.bk_conjugate(
3793
+ MconvPsi
3794
+ ) # [Nbatch, Norient3, Norient2, Npix_j3]
3772
3795
  else:
3773
- s3 = self.backend.bk_expand_dims(conv, -3) * self.backend.bk_conjugate(
3796
+ s3 = self.backend.bk_expand_dims(conv, -3)* self.backend.bk_conjugate(
3774
3797
  MconvPsi
3775
3798
  ) # [Nbatch, Norient3, Norient2, Npix_j3]
3776
3799
  ### Apply the mask [Nmask, Npix_j3] and sum over pixels
@@ -5955,8 +5978,8 @@ class funct(FOC.FoCUS):
5955
5978
  def from_gaussian(self, x):
5956
5979
 
5957
5980
  x = self.backend.bk_clip_by_value(x,
5958
- self.val_min+1E-4*abs(self.val_min),
5959
- self.val_max-1E-4*abs(self.val_max))
5981
+ self.val_min+1E-7*(self.val_max-self.val_min),
5982
+ self.val_max-1E-7*(self.val_max-self.val_min))
5960
5983
  return self.f_gaussian(self.backend.to_numpy(x))
5961
5984
 
5962
5985
  def square(self, x):
@@ -6127,13 +6150,12 @@ class funct(FOC.FoCUS):
6127
6150
  return result
6128
6151
  else:
6129
6152
  if sigma is None:
6130
- tmp = x - y
6153
+ tmp = self.diff_data(x,y)
6131
6154
  else:
6132
- tmp = (x - y) / sigma
6155
+ tmp = self.diff_data(x,y,sigma=sigma)
6156
+
6133
6157
  # do abs in case of complex values
6134
- return self.backend.bk_abs(
6135
- self.backend.bk_reduce_mean(self.backend.bk_square(tmp))
6136
- )
6158
+ return tmp/x.shape[0]
6137
6159
 
6138
6160
  def reduce_sum(self, x):
6139
6161
 
@@ -6295,7 +6317,7 @@ class funct(FOC.FoCUS):
6295
6317
  Jmax=None,
6296
6318
  edge=False,
6297
6319
  to_gaussian=True,
6298
- use_variance=False,
6320
+ use_variance=True,
6299
6321
  synthesised_N=1,
6300
6322
  input_image=None,
6301
6323
  grd_mask=None,
@@ -6362,13 +6384,14 @@ class funct(FOC.FoCUS):
6362
6384
  use_v = args[2]
6363
6385
  ljmax = args[3]
6364
6386
 
6365
- learn = scat_operator.reduce_mean_batch(
6366
- scat_operator.eval(
6367
- u,
6368
- Jmax=ljmax,
6369
- norm='self'
6387
+ learn = scat_operator.eval(
6388
+ u,
6389
+ Jmax=ljmax,
6390
+ norm='auto'
6370
6391
  )
6371
- )
6392
+
6393
+ if synthesised_N>1:
6394
+ learn = scat_operator.reduce_mean_batch(learn)
6372
6395
 
6373
6396
  # compute scattering covariance of the current synthetised map called u
6374
6397
  if use_v:
@@ -6574,6 +6597,8 @@ class funct(FOC.FoCUS):
6574
6597
  )
6575
6598
  sref = ref
6576
6599
  else:
6600
+ self.clean_norm()
6601
+
6577
6602
  ref = self.eval(
6578
6603
  tmp[k],
6579
6604
  image2=l_ref[k],
@@ -6590,7 +6615,7 @@ class funct(FOC.FoCUS):
6590
6615
  mask=l_in_mask[k],
6591
6616
  Jmax=l_jmax[k],
6592
6617
  calc_var=True,
6593
- norm='self'
6618
+ norm='auto'
6594
6619
  )
6595
6620
  else:
6596
6621
  ref = self.eval(
@@ -6598,7 +6623,7 @@ class funct(FOC.FoCUS):
6598
6623
  image2=l_ref[k],
6599
6624
  mask=l_in_mask[k],
6600
6625
  Jmax=l_jmax[k],
6601
- norm='self'
6626
+ norm='auto'
6602
6627
  )
6603
6628
  sref = ref
6604
6629
 
foscat/scat_cov_map2D.py CHANGED
@@ -29,7 +29,7 @@ class funct(scat.funct):
29
29
  image1, image2=image2, mask=mask, norm=norm, calc_var=calc_var, Jmax=Jmax
30
30
  )
31
31
  return scat_cov_map(
32
- r.S2, r.S0, r.S3, r.S4, S1=r.S1, S3P=r.S3P, backend=r.backend
32
+ r.S2, r.S0, r.S3, r.S4, S1=r.S1, S3P=r.S3P, backend=r.backend,
33
33
  )
34
34
 
35
35
  def scat_coeffs_apply(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: foscat
3
- Version: 2025.7.3
3
+ Version: 2025.8.3
4
4
  Summary: Generate synthetic Healpix or 2D data using Cross Scattering Transform
5
5
  Author-email: Jean-Marc DELOUIS <jean.marc.delouis@ifremer.fr>
6
6
  Maintainer-email: Theo Foulquier <theo.foulquier@ifremer.fr>
@@ -2,14 +2,16 @@ foscat/BkBase.py,sha256=2buIR9RK6g7HLoHJbzVCYhi1PkjDW6SXlu7IlF7SfA4,21611
2
2
  foscat/BkNumpy.py,sha256=qvKxDoAPQD52Ui9qv_D_GZvWpXX2n9S9dOGlXz5uNdQ,10683
3
3
  foscat/BkTensorflow.py,sha256=iIdLx6VTOfOEocfZBOGyizQn5geDLTfdWWAwDeQr9YA,20056
4
4
  foscat/BkTorch.py,sha256=fWkNTrgK1MkpkS-bNVmC0ihJY_WlPs98ndperSh63i8,19593
5
- foscat/CNN.py,sha256=gQ9V76wmcowo2BaNp5sJYcSDCVOjc18TS9cE6-qEUso,5153
5
+ foscat/CNN.py,sha256=4vky7jqTshL1aYLWsc-hQwf7gDjTVjL7I6HZiAsa6x4,5158
6
6
  foscat/CircSpline.py,sha256=CXi49FxF8ZoeZ17Ua8c1AZXe2B5ICEC9aCXb97atB3s,4028
7
- foscat/FoCUS.py,sha256=81LmzcWitLGS0CrqddoRmER95JlGxX3N0dQAi1M7i-g,103645
7
+ foscat/FoCUS.py,sha256=_PVSDd4W3surp_dolYUeo_kCd_7x8LohnosBPECvMRk,114643
8
8
  foscat/GCNN.py,sha256=q7yWHCMJpP7-m3WvR3OQnp5taeYWaMxIY2hQ6SIb9gs,4487
9
- foscat/HealSpline.py,sha256=Y05LLtsAVdkzf_u6UZtQx8u1DwfBnmso9OzHAT35ZJM,7387
9
+ foscat/HOrientedConvol.py,sha256=S4Ru4holHT0ux6oMttHrKrXtxF4kGeVvoWE8AQs-RhI,21619
10
+ foscat/HealSpline.py,sha256=YRotJ1NQuXYFyFiM8fp6qkATIwRJ8lqIVo4vGXpHO-w,7472
10
11
  foscat/Softmax.py,sha256=UDZGrTroYtmGEyokGUVpwNO_cgbICi9QVuRr8Yx52_k,2917
11
12
  foscat/Spline1D.py,sha256=rKzzenduaZZ-yBDJd35it6Gyrj1spqb7hoIaUgISPzY,2983
12
- foscat/Synthesis.py,sha256=tC5hvpam19QwDdvghVax7dA7gMgKA6ZtxQEcV9HjdC0,13824
13
+ foscat/Synthesis.py,sha256=ClrUkSKm2426ZX56dHoepoSQ2rJvwTXQnPPtlC2cESY,14174
14
+ foscat/UNET.py,sha256=QIOv3ysz9lGKMFLmyfQarhzzh7gg833hk70Uo04u3-0,7027
13
15
  foscat/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
16
  foscat/alm.py,sha256=XkK4rFVRoO-oJpr74iBffKt7hdS_iJkR016IlYm10gQ,33832
15
17
  foscat/backend.py,sha256=l3aMwDyXP6jURMIvratFMGWCTcQpaR68KnUuuGDezqE,45418
@@ -20,13 +22,13 @@ foscat/loss_backend_torch.py,sha256=k3z18Dj3SaLKK6ZIKcm7GO4U_YKYVP6LtHG1aIbxkYk,
20
22
  foscat/scat.py,sha256=qGYiBIysPt65MdmF07WWA4piVlTfA9-lFDTaicnqC2w,72822
21
23
  foscat/scat1D.py,sha256=W5Uu6wdQ4ZsFKXpof0f1OBl-1wjJmW7ruvddRWxe7uM,53726
22
24
  foscat/scat2D.py,sha256=boKj0ASqMMSy7uQLK6hPniG87m3hZGJBYBiq5v8F9IQ,532
23
- foscat/scat_cov.py,sha256=TgYa78os4vbFI4DRK5hccnH-6bpRpXFEbN52Z2L2Xbs,270032
25
+ foscat/scat_cov.py,sha256=ZJh-aZ_XgvOvsT8aeMgyp9q8bKnUf7hQtC9AGNW4GpI,270857
24
26
  foscat/scat_cov1D.py,sha256=XOxsZZ5TYq8f34i2tUgIfzyaqaTDlICB3HzD2l_puro,531
25
27
  foscat/scat_cov2D.py,sha256=pAm0fKw8wyXram0TFbtw8tGcc8QPKuPXpQk0kh10r4U,7078
26
28
  foscat/scat_cov_map.py,sha256=9MzpwT2g9S3dmnjHEMK7PPLQ27oGQg2VFVsP_TDUU5E,2869
27
- foscat/scat_cov_map2D.py,sha256=1dS4P1KHqZYkYCLA1sYpPSZulJrCTd_2eL8HFOjlcz4,2841
28
- foscat-2025.7.3.dist-info/licenses/LICENSE,sha256=i0ukIr8ZUpkSY2sZaE9XZK-6vuSU5iG6IgX_3pjatP8,1505
29
- foscat-2025.7.3.dist-info/METADATA,sha256=uIMQ9XWdbP1sN8ftu_8flj1OgiNhRXHcn6Rw-tbLoWs,7215
30
- foscat-2025.7.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
31
- foscat-2025.7.3.dist-info/top_level.txt,sha256=AGySXBBAlJgb8Tj8af6m_F-aiNg2zNTcybCUPVOKjAg,7
32
- foscat-2025.7.3.dist-info/RECORD,,
29
+ foscat/scat_cov_map2D.py,sha256=zaIIYshXCqAeZ04I158GhD-Op4aoMlLnLEy7rxckVYY,2842
30
+ foscat-2025.8.3.dist-info/licenses/LICENSE,sha256=i0ukIr8ZUpkSY2sZaE9XZK-6vuSU5iG6IgX_3pjatP8,1505
31
+ foscat-2025.8.3.dist-info/METADATA,sha256=F6_v34qwWdIck5wGbKIKlrVs53dJo-SVITx8HpQf5AM,7215
32
+ foscat-2025.8.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
33
+ foscat-2025.8.3.dist-info/top_level.txt,sha256=AGySXBBAlJgb8Tj8af6m_F-aiNg2zNTcybCUPVOKjAg,7
34
+ foscat-2025.8.3.dist-info/RECORD,,